List Operations

part of Tcl for Web Nerds by Hal Abelson, Philip Greenspun, and Lydia Sandon; updated July 2011
A Tcl list holds a sequence of elements, each of which can be a number, a string, or another list. Let's look at the commands for constructing a list:
% # create an empty list using the list command  
% set user_preferences [list]
% # verify that we've created a 0-item list
% llength $user_preferences
0
% lappend user_preferences "hiking"
hiking
% lappend user_preferences "biking"
hiking biking
% lappend user_preferences "whale watching"
hiking biking {whale watching}
% llength $user_preferences
3
At this point, the variable user_preferences is a three-element list. We can pull individual items out with lindex:
% lindex $user_preferences 0
hiking
% lindex $user_preferences 1
biking
% lindex $user_preferences 2
whale watching
% lindex $user_preferences 3
% lindex $user_preferences 5
Indexing is 0-based and lindex will return the empty string rather than an error if you supply an out-of-range index.

When producing a page for a user, we'd be more likely to be interested in searching the list. The command lsearch returns the index of the list element matching a query argument or -1 if unsuccessful:

if { [lsearch -exact $user_preferences "hiking"] != -1 } {
    # look for new articles related to hiking 
}
Suppose that User A marries User B. You want to combine their preferences into a household_preferences variable using the concat command:
% # use the multiple-argument form of list to create an N-element
% # list with one procedure call
% set spouse_preferences [list "programming" "computer games" "slashdot"]
programming {computer games} slashdot
% set household_preferences [concat $user_preferences $spouse_preferences]
hiking biking {whale watching} programming {computer games} slashdot
% llength $household_preferences
6
The Tcl shell's output is giving you an ugly insight into the internal representation of lists as strings, with elements being separated by spaces and grouped with braces. There is no reason to rely on how Tcl represents lists or even think about it. Practice data abstraction by using Tcl lists as your underlying storage mechanism but define constructor and accessor procedures that the rest of your source code invokes. You won't find a huge amount of this being done in Web development because the really important data structures tend to be RDBMS tables, but here's an example of how it might work, taken from http://philip.greenspun.com/careers/four-random-people.tcl. We're building a list of lists. Each sublist contains all the information on a single historical figure. Method A is quick and dirty:
set einstein [list "A. Einstein" "Patent Office Clerk" "Formulated Theory of Relativity."]

set mill [list "John Stuart Mill" "English Youth" "Was able to read Greek and Latin at age 3."]

# let's build the big list of lists
set average_folks [list $einstein $mill ...]

# let's pull out Einstein's title 
set einsteins_title [lindex $einstein 1]
Method B uses data abstraction:
proc define_person {name title accomplishment} {
    return [list $name $title $accomplishment]
}

proc person_name {person} {
    return [lindex $person 0]
}

proc person_title {person} {
    return [lindex $person 1]
}

proc person_accomplishment {person} {
    return [lindex $person 2]
}

% set einstein [define_person "A. Einstein" "Patent Office Clerk" "Formulated Theory of Relativity."]
{A. Einstein} {Patent Office Clerk} {Formulated Theory of Relativity.}
% set einsteins_title [person_title $einstein]
Patent Office Clerk
Data abstraction will make building and maintaining a large system much easier. As noted above, however, the stateless nature of HTTP means that any information you want kept around from page to page must be kept by the RDBMS. SQL already forces you to refer to data by table name and column name rather than positionally.

Split and Join

Suppose we have a file called addressees.txt with information about people, one person to a line. Suppose each of these lines contains, among other information, an email address which we assume we can recognize by the presence of an at-sign (@). The following program extracts all the email addresses and joins them together, separated by commas and spaces, to form a string called spam_address that we can use as the Bcc: field of an email message, to spam them all:
# open the file for reading
set addressees_stream [open "~/addressees.txt" r]

# read entire file into a variable
set contents_of_file [read $addressees_stream] 

close $addressees_stream

# split the contents on newlines
set list_of_lines [split $contents_of_file "\n"]

# loop through the lines 
foreach line $list_of_lines {
    if { [regexp {([^ ]*@[^ ]*)} $line one_address] } {
        lappend all_addresses $one_address
    }
}

# use the join command to mush the list together
set bcc_line_for_mailer [join $all_addresses ", "]
Some things to observe here:

Reference: List operations



Continue on to Pattern matching.
Return to Table of Contents

lsandon@alum.mit.edu

Reader's Comments

There is a way of sorting strings according to lists inside the list. If you have a list of items and values, you can then sort by the values.

See: http://tcl.activestate.com/man/tcl8.2.3/TclCmd/ lsort.htm#M12 and http://openacs.org/bboard/q-and-a-fetch-msg.tcl?msg_id=0005vw&topic_id=11&topic=OpenACS

-- Jade Rubick, August 23, 2002

Add a comment | Add a link