TCLWISE
An introduction to the Tcl programming language

Sponsored Project: The Jim interpreter
A small footprint implementation of Tcl

Send a comment to the author


4. LISTS


4.1 Tcl list


In Tcl everything is represented as a string. Lists don't escape this fundamental rule of the language, instead the string representation of lists is very direct to think for humans. The simplest form of Tcl list is a string with zero or more space-separated words. For example the string "a b c" is a three-element list, you can test it using the llength command, it gets as argument a Tcl list, and returns the number of elements present in such a list:

% llength "a b c" 3

The empty list is represented by an empty string, that's "" or {}.

% llength {} 0

There is also a command that extracts a given element from a list called lindex:

% lindex "this is a list" 1 is

As you can see, lists indexes are zero-based. The first element is at index 0 ("a" in our example), the second is at index 1, and so on. Lindex, and generally every list-related command that takes a list index as argument accepts also "end" as index, that is used to specify the last element of the list. There is also the form "end-<number>", that referes to the last-<number> element of the list. Some example:

% set mylist "foo bar biz! Tcl" foo bar biz! Tcl % lindex $mylist end Tcl % lindex $mylist end-1 biz!

You may wonder how it's possible to create a list containing elements that have spaces inside. The solution was already shown actually, and is called grouping:

% llength "a b {c d} e" 4

But when you need to write complex lists, the way to go is to use a command that was created just for this, that's the list command:

% list a b "c d" e a b {c d} e

List creates a list using every input argument as an element of the list. We can create every kind of list with it, and of course nested ones:

% set mylist [list a b [list 1 2 3 4] c d] a b {1 2 3 4} c d % lindex $mylist 2 2 3

As you can see the lindex command can be used to directly access nested lists. "lindex $mylist 2 2" means access the second element, of the second element, of the list in $mylist.

4.2 The foreach command


Foreach is a very powerful command used to execute a Tcl scripts for every element of one or more lists. The command signature in its simplest usage is:


foreach varname list script


The following is an example to print the square of the first five natural numbers on the screen.

set mylist {1 2 3 4 5} foreach e $mylist { puts [expr $e*$e] }

For every element of the list, foreach assign its value to the specified variable and execute the script. Actually instead of a single variable the user may provide a list of variables, like in the following example:

set mylist {1 2 3 4 5 6} foreach {x y} $mylist { puts "$x+$y = [expr $x+$y]" }

This program will output:

1+2 = 3 3+4 = 7 5+6 = 11

Basically when the user passes a list of N variables, for every iteration N new elements of the list are consumed to be assigned to the specified variables, and the script is executed. Foreach can do a bit more, accepting more than a variables/list pair like in the following example:

set firstlist {1 2 3 4 5 6} set secondlist {a b c d e f g} foreach {a b} $firstlist {c d} $secondlist { puts "$a$c,$b$d" }

This time the output will be:

1a,2b 3c,4d 5e,6f

It's the simple usage but with multiple lists walked in parallel. As you can see foreach is a very flexible command for a data structure that's central in Tcl, so you will see this command used in almost every non trivial Tcl program.

4.3 The lrange command


Lrange extracts a sublist from a list. The sublist to extract is specified by a start and end index, indexes are as usually zero-based, and it's possible to use the "end-<integer>" notation. Example:

% lrange {Tcl is a programmable programming language} 2 end-1 a programmable programming %

4.4 The lappend command


At this point we know a few commands that operate on lists: llength, lindex, foreach, lrange. They have something in common because all take a Tcl list as input. the lappend command instead takes as its first argument the name of a variable containing a list, and a variable number of other arguments. Lappend get the list stored in the specified variable, append every other argument to the list (every argument as a new list element), and set the variable to the value of the new obtained list. The value of the list obtained is also returned as lappend's return value:

% set a {foo bar} foo bar % lappend a biz buz foo bar biz buz %

If the specified variable does not exists, it will be created (and lappend will assume the previous content was the empty list).

With lappend we are able to create a user defined procedure that given an input list, returns a list composed of the same elements in reversed order. A good name for such a procedure is lreverse, and it is very simple to write:

proc lreverse l { set result {} set i [llength $l] incr i -1 while {$i >= 0} { lappend result [lindex $l $i] incr i -1 } return $result }

The usage is simple too:

% lreverse {a b c} c b a

In the lreverse implementation there are two core commands that we never used. The first in order of apparition is incr, the second is while. Incr is very simple, it takes two arguments: the name of a variable, and an integer. It gets the value of the variable that must be an integer number, add the second argument to it, and set the result as the new value for the variable.

If the variable a is set to 6, the command "incr a 2" will set it to 8, while "incr a -1" will set it to 5, and so on.

The second argument, while, takes two arguments, the first is an expression, the second a script. The script is executed while the expression continues to be true. The expression, like it happens for the if command, is evaluated using the expr command internally.

4.5 The lset command


The lset command is in some way the complement to lindex. While lindex returns the list element at the specified index, lset replaces the list element at the specified position with a new value. Like lappend, lset takes as input a variable name containing a list, and directly modify the value of the variable instead to just return a modified list. An example of the lset usage:

% set a {a b c d} a b c d % lset a 2 X a b X d % puts $a a b X d

Lset can directly access nested lists in a similar fashion to lindex, specifing multiple indexes, so for example the command "lset foobar 2 3 $newval" Will set $newvalue as third element of the list that's the second element of the list contained inside the variable $foobar.

4.6 The lsort command


As suggested by the name, the lsort takes a list in input, and returns it sorted as return value. The command structure is:


lsort ?options? list


Note that in Tcl it is well accepted to show the command structure surrounding with two question marks the argument(s) that may be omitted in the actual command usage (practically this means that the lsort command can be called with just an argument: the list).

Options are just strings with a special meaning for the lsort command, and are prefixed with a "-" character as it's usual with Tcl commands (but note that lsort knows that all the options except the last are arguments). The goal of options is to modify the way lsort sorts, and can be combined to get specific effects. Not all the options will be exaplained here, please check the manual page if you want the details.

Some example:

% set mylist [list this Tcl book have more than 2 chapters and 10 pages] this Tcl book have more than 2 chapters and 10 pages % lsort $mylist 10 2 Tcl and book chapters have more pages than this % lsort -decreasing $mylist this than pages more have chapters book and Tcl 2 10 % lsort -dictionary $mylist 2 10 and book chapters have more pages Tcl than this %

The first example calls lsort without options, but just with the list to sort: if invoked in this way lsort sorts the list comparing using ASCII value of letters (actually unicode is used, but for now you can ignore this expecially if you are not using unicode strings).

The second example adds the -decreasing option to reverse the sorting order. The third example changes the comparison algorithm specifing the -dictionary option: this mode is smarter for many uses as it ignore the case of the letters and is able to deal better with numbers (if a string contain a number, the value of the number is compared by value). The effect is similar to the order of lemmas in a dictionary, thus the name.

An interesting use of lsort is due to the -unique option that returns every element only one time if it's repeated:

% lsort -unique {foo bar bar bar x y y y z foo} bar foo x y z

It may be comfortable to include this functionality in a library directly as a Tcl procedure called unique. The implementation is simple:

proc unique l { lsort -unique $l }

4.7 List values against variable names


Maybe it's just too early to ask complex questions about Tcl, but some reader may wonder why there are Tcl list commands working in a functional way, that's operating directly on the value of the list to return another list as result, and commands that instead takes the name of a variable where a list is stored.

For now it's enough to know that there are efficiency reasons because one approach is more convenient than the other. The second reason is that actually it is more common to use lset and lappend against lists already stored in variables.

Of course, as it's the norm with Tcl, if you need to have similar procedures operating with values you are free to implement they. Actually there are already procedures that can be used for the scope, that are lreplace and concat. If you are interested take a look to the manual pages of this functions. Just as example the following is a function similar to lset, that takes as arguments a list, an index, and a new value, and returns a list where the specified index is replaced with the new value:

proc mylset {mylist index newval} { lset mylist $index $newval }

It's written in terms of lset, because lset already returns the value of the modified list. The usage is simple:

% mylset {a b c d} 2 foo a b foo d %

Note that mylset will not alter in any way the list passed as argument. In Tcl, procedure's arguments are local variables, and arguments are passed by value. Note how mylset will not alter the list stored into the a variable:

% set a {a b c d} a b c d % mylset $a 2 foo a b foo d % set a a b c d

The a variable continues to contain the list a b c d, Tcl is a safe language, it is not possible to alter the value of a variable passed as argument unless the procedure is written in a special way, but in such a case the user must pass the name of the variable, and not it's value substituted using the $ character before the variable name. We will see all this things better later.

Other Tcl/Tk books
Index
2.1 Anatomy of a command
2.2 Grouping
2.3 Program structure
2.4 Substitution of commands
2.5 Substitution of variables
2.6 More on interpolation
2.7 Comments
2.8 That's it
3.1 User defined procedures
3.2 The if command
4.1 Tcl list
4.2 The foreach command
4.3 The lrange command
4.4 The lappend command
4.5 The lset command
4.6 The lsort command
4.7 List values against variable names
5.1 The append command
5.2 The string command
5.3 string range
5.4 string index
5.5 string equal
5.6 string compare
5.7 string match
5.8 string map
5.9 string is
5.10 More string subcommands
5.11 Advanced string matching
6.1 Converting strings to lists
6.2 From strings to list of chars
6.3 Converting lists to strings
6.4 Manipulating strings as lists
7.1 Local variables
7.2 Top level
7.3 Global variables
7.4 Procedures arguments and pass by value
7.5 Procedures with a variable number of arguments
7.6 Procedures with default arguments
7.7 Recursion
7.8 Recursion limit
8.1 The switch command
8.2 The for command
8.3 break and continue
8.4 The lack of goto
9.1 Programs executing programs: the eval command
9.2 Breaking the rules with uplevel
9.3 Passing variable names to procedures
9.4 Mapping scripts to lists
9.5 The rename command
9.6 Expanding lists into arguments in Tcl 8.5
Additional 20 chapters in the printed version.

Related man pages


Links
Author Home
Tclers Wiki


Copyright 2004 Salvatore Sanfilippo. All rights reserved.
This online book is for personal use only.
It cannot be copied to other web sites or further distributed in any form.