% # 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.
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:
foreach
operator (see the
chapter on control
structure) to iterate over the list formed by splitting the file
at newline characters.
regexp
in the chapter on pattern matching.)
lappend
ing to
all_addresses
, but this variable is never initialized.
lappend
treats an unbound list variable the same as an empty list.
(list arg1 arg2...)
in Scheme.
set foo [list 1 2 [list 3 4 5]] ==> 1 2 {3 4 5}
http://www.tcl.tk/man/tcl8.4/TclCmd/list.htm
lindex list i
lindex $foo 0 ==> 1
http://www.tcl.tk/man/tcl8.4/TclCmd/lindex.htm
llength list
llength $foo ==> 3
http://www.tcl.tk/man/tcl8.4/TclCmd/llength.htm
lrange list i j
lrange $foo 1 2 ==> 2 {3 4 5}
http://www.tcl.tk/man/tcl8.4/TclCmd/lrange.htm
lappend listVar arg arg...
set
works.
lappend foo [list 6 7] ==> 1 2 {3 4 5} {6 7}
set foo ==> 1 2 {3 4 5} {6 7}
http://www.tcl.tk/man/tcl8.4/TclCmd/lappend.htm
linsert list index arg arg...
linsert $foo 0 0 ==> 0 1 2 {3 4 5} {6 7}
http://www.tcl.tk/man/tcl8.4/TclCmd/linsert.htm
lreplace list i j arg arg...
lreplace $foo 3 4 3 4 5 6 7 ==> 0 1 2 3 4 5 6 7
set foo ==> 1 2 {3 4 5} {6 7}
http://www.tcl.tk/man/tcl8.4/TclCmd/lreplace.htm
lsearch mode list value
set community_colleges [list "caltech" "cmu" "rpi"]
lsearch -exact $community_colleges "caltech" ==> 0
lsearch -exact $community_colleges "harvard" ==> -1
http://www.tcl.tk/man/tcl8.4/TclCmd/lsearch.htm
lsort switches list
set my_friends [list "herschel" "schlomo" "mendel"]
set my_sorted_friends [lsort -decreasing $my_friends] ==> schlomo mendel herschel
http://www.tcl.tk/man/tcl8.4/TclCmd/lsort.htm
concat arg arg...
set my_wifes_friends [list "biff" "christine" "clarissa"]
concat $my_wifes_friends $my_friends ==> biff christine clarissa herschel schlomo mendel
http://www.tcl.tk/man/tcl8.4/TclCmd/concat.htm
join list joinString
set foo_string [join $foo ":"] ==> 0:1:2:3:4:5:6:7
http://www.tcl.tk/man/tcl8.4/TclCmd/join.htm
split string splitChars
set my_ip_address 18.1.2.3 ==> 18.1.2.3
set ip_elements [split $my_ip_address "."] ==> four element list,
with values 18 1 2 3
http://www.tcl.tk/man/tcl8.4/TclCmd/split.htm
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