Updated 2013-05-08 04:59:53 by pooryorick

Name  edit

list - Create a list

Synopsis  edit

list ?arg arg ...?

Documentation  edit

man page

Description  edit

This command returns a string formatted as a Tcl list, where each list element corresponds to each arg, or an empty string if no args are specified.

A list is an ordered tuple of values. In other languages this is sometimes known as an array or vector. Every list has a length, which is an integer >=0, and the llength command returns the length of the list.

To retrieve the element at a particular position, one uses the lindex command. The first element is at position 0.

New lists can be constructed using the list command.

Tcl lists are not fixed-length once created, as is the case in some other languages. Elements can be appended to a list stored in a variable using the lappend command.

Constructing a List  edit

Given the following command,
list \n

the value will be
{
}

In order to form a valid list value, the list command inserts backslashes and braces as necessary:
list \n\{

The result:
\n\{

Using List to Concatenate Lists  edit

[list] can be used with {*} to the same effect as [concat]:
set a {a b c}; set b {d e f}
list $a $b                    --> {a b c} {d e f}
concat $a $b                  --> a b c d e f
list {*}$a {*}$b              --> a b c d e f

See Concatenating lists for a timing comparison of the various methods.

String Representation  edit

The string representation of a list is similar to the string representation of a Tcl script as defined by the Dodekalogue. One difference is that in a script, a newline is considered a command terminator, but for a list, a newline is just white space that separates words. Also, the following rules are not applied to lists:

  • commands
  • Argument expansion
  • variable substitution
  • command substitution
  • Comments

It is possible to use lists anywhere one can use arbitrary strings: write to a channel with puts, use as key in an array or dict, show to the user in an entry widget, etc.

eval may be used to evaluate lists which conform to script syntax.

All Tcl lists are strings, but not all strings are valid lists. The various list functions expect their arguments to be strings representing valid lists. See below for examples of strings which are not valid lists. For documentation about the string representation of a list, see generic/tclUtil.c in the Tcl source code, and also see Tip 407 ,Tip 148.

Behind the scenes, Tcl tracks the structure of the list, and the various list commands take advantage of this to improve performance to O(1) time (access time does not depend on the total list length or position within the list). The Tcl programmer should be aware that manipulating lists using string operations can incur a dramatic performance penalty. The rule of thumb is to always use list-aware commands.

A list can be (inefficiently) extended by another list using a string operation:
append baseList " $extraList"

or
set baseList [concat $baseList $extraList]

The same thing can be efficiently accomplished with a list command:
lappend baseList {*}$extraList

before the advent of the {*} operator, the following syntax was used:
eval [list lappend baseList] $extraList

The literal string representation of lists is used in the following cases:
List constants
In some cases, a command accepts a list for an argument, but practically everyone writes that list as an explicit brace-delimited word. This is true for e.g. proc (the arguments command is a list; in fact, it is a list of lists), switch (in the braced patterns-and-bodies form), and string map (the map is a list). These are usually not a problem, but you may need to think about the list syntax when special characters (backslash, braces, whitespace, quotes) are involved. Sometimes, an extra layer of braces are required around a list element, but if it is unbalanced with respect to braces then you may need to backslash-escape all special characters in it instead.
Command construction
Tcl is a dynamic language which allows for the construction of Tcl scripts at runtime. When substituting some piece of information into the dynamically-generated script, it is usually necessary to subtitute in a list, even if the context requires a single value. string map is most often used for this purpose. list and make use of the result from list, even though it is in a "string" context. See Tcl Quoting for more information on this.

There are multiple ways to represent the same list value. The following two values represent the same list value:
set a {one two three}
set b {one  two three}

Internal Representation  edit

The internal representation of a list should be transparent at the script level, but for the curious:

In the C implementation of Tcl 8.x, a list is a type of Tcl_Obj, The elements of a list are stored as but rather as C-style vectors of pointers to the individual Tcl_Obj element values, plus some extra data. An important consequence of this is that llength and lindex are constant time operations — they are as fast for large lists as they are for small lists.)

List Operations  edit

The list commands in Tcl are for the most part the commands that begin with l (except for load): lappend, lassign, lindex, linsert, list, llength, lrange, lrepeat, lreplace, lreverse, lsearch, lset, and lsort. The concat command also operates on lists, but does not require that its arguments be valid lists. foreach operates on lists. split creates lists, and join are consumes them. Various other commands also make use of lists.

Since all lists (and indeed, all values) are strings in Tcl (but not all strings are lists!), it is possible to use string commands on lists, but in cases where it is not an outright error to do so, performance can suffer and there are usually better ways to accomplish the task. Perhaps the most common example of such a mistake is to compare a list to the empty string to see if it is empty:
#example of bad coding practice
proc foo {bar args} {
    if {$args eq {}} then { #string comparison might force internal Tcl gyrations
        set args $::foo::default_for_args
    }
    # ...
}

The better alternative is to use llength:
proc foo {bar args} {
    if {![llength $args]} then { # $args is empty
        set args $::foo::default_for_args
    }
    # ...
}

Strings that are not Lists  edit

Because lists are so pervasive in Tcl, one of the common beginner misconceptions is that all values are lists. This is not the case. When in doubt, use [string is list ...]. A string is not a list if literal (unescaped) { and } characters are not balanced. At this time, the best way to test a string to see if it is a list would be
catch {llength $somevalue}

When doing experiments to understand these matters, it is a good idea first assign the values in question to variables before operating on them, since it is hard to keep track of which quoting is interpreted when the command is parsed and which is parsed by the command being invoked. This makes it possible to first inspect the value of the string before passing it to a command:
set somevariable 

In the following examples, values that are not lists are assigned to the the variable, notalist.
set notalist "{}a"
set a {{a b} {c}]}
set notalist "ab\{ \{x y"

In the previous two examples, the values aren't lists because they violate the Braces and words rules, which state that a braced word is terminated by a matching closed brace, and that words are separated by whitespace

Layers of Interpretation  edit

In the following example, the value of a_single_backslash is a single backslash:
set a_single_backslash [lindex \\\\ 0]

prior to evaluating the lindex command, Tcl performs backslash substitution on the four backslashes, so that the first argument becomes two backslashes. To convert the first argument to a list, the [lindex] command then performs backslash on substitution on the two backslashes, resulting in one backslash, which is then assigned to a.

Converting a String to a List  edit

The split takes a string and returns a list, but the best choice depends on the task at hand. regexp is often handy:
set wordList [regexp -all -inline {\S+} $myGnarlyString]

Creating a List from Arbitrary Input  edit

DKF proposed this pretty alias:
interp alias {} listify {} regexp -all -inline {\S+}

[Bill Paulson] notes that this alias changes all white space to a single space, which might or might not be what you want.

Other than that, this listify is effectively a split that throws away all empty elements.

Validating a List  edit

Various ways to check whether a string is a list:
string is list $some_value
catch {lindex $some_value 0}
catch {llength $some_value}

In later versions of Tcl, string is list is available.

List Vs. List of Lists  edit

escargo 2003-03-16: How can you tell if a value is a string of words or a list of strings of words?

The practical application that I had for this was an error-printing proc. It could be passed a value that might be a single error message or a list of error messages. If it were a single error message, then I could print it on one line; if it were multiple messages, then I wanted to print each on its own line.

So, how could I distinguish between the cases?

I think I eventually made all sources of errors provide a list of errors, even if was always a list of 1 (instead of just the error message string).

But the question always stuck with me? Was there a way I could have easily distinguished between the two? Could I look at the representation and see an opening curly brace if it were a list?

RS: The (outer) curlies are not part of the list - they are added, or parsed away, when needed.

Tcl lists are not fundamentally different from strings, rather, I'd say they are a "view" on strings. Just as "42" can be viewed as string, or integer, it can also be viewed as a one-element list. Except if you introduce your own tagging convention, there is no way of telling whether a list is in reality a string - in the other direction, only strings that cannot be parsed as lists (unbalanced braces, quotes..) cannot be viewed as lists. But for your concrete error-printing problem: if you simplify the interface to "a list of one or more error messages", you can have the desired effect with
puts [join $errormessages \n]

Just make sure that the "one message" case is properly nested, e.g.
errorprint {{This is a one-liner}}
errorprint "{This too, with a $variable reference}" ;# braces in quoted strings allow substitution
errorprint [list "another $variable reference"]
errorprint {{Two messages:} {This is the second}}

Single-item Lists vs non-list value  edit

Many simple string values can also be interpreted as single-element lists. Programs should use additional data or rely on program logic to decide whether a value should be interpreted as a list or a string.

Here is another way of looking at the problem (lindex without an index returns the list argument unchanged):
% lindex a
a
% lindex a 0
a
% lindex [lindex a 0] 0
a
% lindex [lindex [lindex a 0] 0] 0
a
% lindex {a}
a
% lindex {a} 0
a
% lindex [lindex {a} 0] 0
a
% lindex [lindex [lindex {a} 0] 0] 0
a
% lindex {{a}}
{a}
% lindex {{a}} 0
a
% lindex [lindex {{a}} 0] 0
a
% lindex [lindex [lindex {{a}} 0] 0] 0
a
% lindex {{{a}}}
{{a}}
% lindex {{{a}}} 0
{a}
% lindex [lindex {{{a}}} 0] 0
a
% lindex [lindex [lindex {{{a}}} 0] 0] 0
a

No program can tell the difference between the string "a" and the one-element list "a", because the one-element list "a" is the string "a".

Dossy 2004feb26: A co-worker yesterday who is new to Tcl discovered something that surprised me -- nested lists in Tcl don't work as I expected in a very specific case:
% list [list [list x]]
x

Um, when I ask for a list of a list of a list with the single element 'x', I would expect '{{{x}}}' back. However, you just get 'x' back. Thinking about it, I understand why, but it means that Tcl lists alone cannot be used to represent ALL kinds of data structures, as Tcl lists magically collapse when it's a series of nested lists with the terminal list having only a single bareword element that requires no escaping.

Lars H: It looks worse than it is. For one thing, it is only the string representation that collapses, not the internal representation, so the above nesting of [list] is not completely pointless. It is furthermore very uncommon (and this is not specific to Tcl) that nesting depth alone has a significance. Either you know the structure of the value, and thereby the intended nesting depth, or the list is some generic "thing", and in that case you anyway need a label specifying what kind of thing it is.

[DBaylor]: I think this is actually worse than it looks. I see lots of people trying to learn Tcl and the #1 point of confusion is Dossy's example. But what I really dislike about this behavior is that it hides bugs until specific input is encountered. If you ever mix up your data-types (string vs. list), your code will work fine 99% of the time - until special characters are involved. These bugs are inevitably found the hard way. Oh how I wish [list x] returned {x}.

Subsetting a List  edit

LV Question: in Perl, Python, and a number of other languages, one has the ability to read and write to subsets of a list -- slices -- using an almost array like notation. Is this something that one could simulate without much grief in Tcl?

RS But of course - our old friends (with wordier notation)
set slice [lrange $list $from $to]
set newlist [eval lreplace [list $otherlist] $from $to $slice]

The new lset can only replace a single element, but possibly several layers deep in the nesting. For reading access to a slice of a list, check Salt and sugar for how the following is implemented:
set i [$ {a b c d e f g} 2 4] ==> {c d e}

Flattening a List  edit

To flatten a list:
concat {*}$nested

It can be applied multiple times:
proc flatten {data} {
    concat {*}$data
}
set a {{a {b c}} {d {e f}}}  ; # {a {b c}} {d {e f}}
flatten $a                   ; # a {b c} d {e f}
flatten [flatten $a]         ; # a b c d e f

alternatively:
set flattened [join $mylist]

Another option:
eval concat [lrange $mylist 0 end]

Yet another possibility:
foreach e $list {
    foreach ee $e {
        lappend flatList $ee
    }
}

LV: I recently had a need to turn a list which could potentially contain elements which are lists into a list of individual elements. Apparently, this is commonly referred to as flattening the list. [ericm] suggested the following code to solve the problem:
proc flatten {args} { 
    foreach arg $args {puts $arg} 
} 
set foo [list [list a b] [list c d] [list e f]] 
eval eval flatten $foo 

RS shudders at eval eval

The following example is not sufficient unless $mylist is a pure list.
#warning, example of bad code
eval concat $mylist

If the list has a string representation that includes newline whitespace, which is often the case for long hand-coded lists, this error will result:
% eval concat {a
b}
ambiguous command name "b": binary break

Newlines in the arguments end the concat command, and start a new one. This is due neither to concat nor to the tclsh prompt loop, but to the nature of eval. If you don't want to worry about typing multiline commands at the prompt, then use substitution to put the newline in there:
% eval concat "a \n b"
ambiguous command name "b": bell binary bind bindtags break button

RS 2004-02-26: If you really want to flatten a list of any depth, i.e. remove all grouping, I think this way is simplest (and robust):
proc flatten list {
    string map {\{ "" \} ""} $list
}
% flatten {a {b {c d {e f {g h}}}}}
a b c d e f g h

Lars H: No, that won't work. Consider
% flatten [list \{ \}]
\ \

I'd give you that it isn't exactly clear what should happen to lists with such elements, but the above doesn't get a single character right. - RS admits he was thinking of well-behaved lists (as built with list and lappend, where braces are only generated mark-up, not content :^) You're right to point out that flatten is not chimpanzee-proof, and robust, enough.

[cyrilroux] 2010-10-28: Maybe here is the solution to solve the escape issue? This always consist in replacing all { and } but NOT \{ and \} (escaped ones). That is to say 6 cases :

"^{" " {" "{{+" "}$" "} " "}}+"
proc lflatten { list } {
    regsub -all {^\{| \{|\{\{+|\}$|\} |\}\}+} $list { } flatten
    return $flatten
}

% set foo {{a {b c}} {d\{d\{ {e f} g}}
% lflatten $foo
a b c  d{d{ e f g 

CMP: In general, string manipulation on lists all have the same problem; they do not consider the list structure (unless copying the complete implementation of the existing list commands). Hence, all list manipulations should be done using existing list commands.

Information about struct::list - extended list operations  edit

Tcllib now contains a struct::list module. Its documentation can be found at http://tcllib.sourceforge.net/doc/struct_list.html.

dgp offers this example of making use of it:
% package require struct 1.3
1.3
% namespace eval my {
    namespace import ::struct::list
    set l [::list 1 2 3]
    puts [list reverse $l]
}
3 2 1

More explanation…

In the above example, is the use of ::list in the set command vs list in the puts command merely an example of different ways of referring to portions, or does one refer to the original tcl list versus the imported new list command?

MG I believe ::list in the above refers to the Tcl list command, and list refers to the imported ::struct::list (which is then also ::my::list, as it's imported). Or at least I think that's right. Namespaces used like that require a lot more sleep than I've had lately ;)

What would a Tcl version of the list command look like?  edit

AMG: Is this an acceptable implementation of [list]?
proc list {args} {
    return $args
}

Looks right to me... - RS To me too. That's because args is already a list, as by the Tcl parser... I'd just write
proc list args {set args}

AMG: Chuckle.
proc list args [list set args]

Transform a list into a list of fixed size lists  edit

LV Jonathan Bromley wrote, on May 30, 2008, in comp.lang.tcl, the following little tcl proc for turning a long list into a list of lists (in response to a user's query)
proc split_list {L {n 50}} { 
    incr n 0; # thanks to RS for this cool "is it an int" check! 
    set result {} 
    set limit [expr {[llength $L] - $n}] 
    for {set p 0} {$p <= $limit} {incr p $n} { 
        lappend result [lrange $L $p [expr {$p+$n-1}]] 
    } 
    return $result 
} 

[arg]: Just the code I wanted (needed to split results from SQLite). Thanks. But can I suggest a few tweaks:-

  1. name changed to "partitionlist", I think it's less ambiguous than "split_list". Any ideas for a better name?
  2. change default from 50 to 2, splitting into pairs looks a more useful default.
  3. change setting/comparing "limit" so that all the elements of the original list are copied, the original version acted as if the orignal list were truncated to a multiple of the requested sublist length. This version will output any "extra" elements as another short sublist.
proc partitionlist {L {n 2}} { 
    incr n 0; # thanks to RS for this cool "is it an int" check! 
    set result {} 
    set limit [llength $L] 
    for {set p 0} {$p < $limit} {incr p $n} { 
        lappend result [lrange $L $p [expr {$p+$n-1}]] 
    } 
    return $result 
} 

Lars H: If the partitioned list is then immediately going to be iterated over, it may of course be easier to take advantage of the fact that the variable arguments of foreach are really lists of variables. I.e., instead of
foreach row [partitionlist $data 3] {
    lassign $row first second third
    # Further processing...
}

one can just do
foreach {first second third} $data {
    # Further processing...
}

Conversely, this can be used for the following braintwisters' implementation of partitionlist:
proc partitionlist {L {n 2}} {
    set varlist {}
    set body {lappend res [list}
    for {} {$n>0} {incr n -1} {
        lappend varlist $n
        append body { $} $n
    }
    set res {}
    foreach $varlist $L [append body \]]
    return $res
}

Other related topics  edit

caspian recommends that you see Tcl list related commands such as:

Some Tcl core commands require lists as argument or return lists

Making lists with list vs lappend  edit

An interesting observation came up on comp.lang.tcl today:
Example 1: 
$ tclsh8.6
% set l [list a b c] 
a b c 
% llength $l 
3 
% lappend l [list d e f] 
a b c {d e f} 
% llength $l 
4  
% lappend m [list a b c] 
{a b c} 
% llength $m 
1 
% lappend m [list d e f] 
{a b c} {d e f} 
% llength $m 
2 

While a developer might think that creating a variable using the results of list would be the same as creating a variable using the results of lappend, the results are in fact different.

On the other hand,
$ tclsh8.6
% lappend m a b c
a b c
% lappend l [list a]
a
%

If the values to lappend are not multi-element lists, then the results will be the same.

AMG: What's the difference between a single-element list and the element itself?

Other uses of [list]  edit

AMG: When given no arguments, the [list] command returns empty string. I find this useful when entering Tcl commands interactively, e.g. into tkcon or tclsh. When I know that a command will produce a lot of output, such as [read]'ing a whole file, and I don't want to have it all on my screen, I tack "; list" onto the end of my command line.
$ tclsh
% set chan [open bigfile]
file12dcd00
% set data [read $chan]; list
% close $chan

If not for the "; list", the second line would flood my terminal with lots and lots of garbage.

AMG again: Another use for list is to pass it a single argument which it will then return. See SCT & RS's comments on the if page for an example usage. However, this only works due to the "problem" noted above by Dossy 2004feb26. Often the argument requires quoting to become a single-element list, in which case list will not return its argument verbatim. On the return page I discuss a few other, safer approaches. The simplest one is to instead use single-argument lindex.

See also:  edit