Introducing Tcl 8.7 Part 2: list enhancements

Published

This is the second in a series of posts about the new features in the recently released alpha version of Tcl 8.7. This post deals with the enhancements to list processing.

To take Tcl 8.7 for a spin, you can download a pre-alpha binary for your platform. Alternatively, you can build it yourself from the core-8-branch branch in the Tcl fossil repository.

None of the enhancements described here add fundamental new capabilities to Tcl that could not be accomplished at the script level in earlier versions. However, script level implementations are trickier than might seem at first glance, having to deal with non-numeric relative indexing (e.g. end-2), nested lists and unordered indices in operations that modify the list. In addition, there is the obvious benefits of "native" implementations in terms of speed.

The lpop command

The new lpop command extracts an element from a list stored in a variable and returns it while storing the modified list (with the element removed) back in the variable. It has the syntactic form

lpop LISTVARNAME ?INDEX ...?

As always, INDEX may be an integer, the keyword end signifying the last element or a simple expression as illustrated in the following.

% set L {a b c d e f g}
a b c d e f g
% puts [lpop L],$L
g,a b c d e f
% puts [lpop L end-2],$L
d,a b c e f
% puts [lpop L [lsearch $L b]+1],$L
c,a b e f
% puts [lpop L 0],$L
a,b e f

Notice INDEX defaults to end if unspecified.

If multiple indices are specified, they reach into nested lists.

% set L {a b {c d {e f}} g}
a b {c d {e f}} g
% puts [lpop L end-1 end 0],$L
e,a b {c d f} g

Unlike commands such as lindex, lpop will raise an error if any index is out of range.

The lremove command

Another new command for operating on lists is lremove which, like lpop, removes elements from a list. It differs in that, unlike lpop, it operates on list values, not variables, can remove multiple elements and returns the modified list, not the elements removed.

lremove LIST ?INDEX ...?

Unlike lpop, if multiple indices are specified, they indicate multiple elements to be removed, not a path to an element in a nested list structure. Indices may be specified in arbitrary order and may be duplicated.

% lremove {a b c d e f g} end-1 2 5
a b d e g

Note the duplicate and out of order indices.

The -stride option to lsearch

Lists of records are commonly represented in Tcl in one of two ways:

  • a nested list where the inner list is a record
  • a flat list where fields of the records are implicitly grouped together.

For example, a record consisting of cities and population in millions may be represented either as either of the following forms:

set nested [list {Tokyo 37} {Delhi 29} {Shanghai 26}]
set flat   [list Tokyo 37 Delhi 29 Shanghai 26]

For the most part, Tcl 8.6 provided commands for either of these styles. For example, the -index and -stride option to sort nested and flat lists respectively.

% lsort -index 0 $nested
{Delhi 29} {Shanghai 26} {Tokyo 37}
% lsort -stride 2 $flat
Delhi 29 Shanghai 26 Tokyo 37

The -stride option specifies the number of elements in the list that are considered to be one record or group.

However, lsearch in Tcl 8.6 supported the -index option to search through nested lists,

% lsearch -index 0 $nested Delhi
1
% lsearch -index 0 -inline $nested Delhi
Delhi 29

it did not provide an equivalent -stride option for searching flat lists. Tcl 8.7 fixes this deficiency.

% lsearch -stride 2 $flat Delhi
2
% lsearch -stride 2 -inline $flat Delhi
Delhi 29

By default, lsearch will compare the pattern against the first element in each stride group. The -index option may be used to change this. For example, suppose we wanted to match against the last name in a flat list of first and last name pairs.

% set names {John Lennon Elton John John Legend}
John Lennon Elton John John Legend
% lsearch -stride 2 -index 1 $names John
2
% lsearch -stride 2 -index 1 -inline $names John
Elton John

Note above that the index returned is that of the first element in the matched group, not the matched element.

The -stride option may be used with other combinations of options as well. See the manpage for more examples.

References

TIP 523: New lpop command

TIP 367: A Command to Remove Elements from a List

TIP 351: Add Striding Support to lsearch

lpop manpage

lremove manpage

lsearch manpage