You don't have to declare to Tcl that you're going to treat a particular variable as an array; just start setting variables with the form "variable_name(key)".% set numeric_day(Sunday) 0
0
% set numeric_day(Monday) 1
1
% set numeric_day(Tuesday) 2
2
% # pull one value out of the hash table
% set numeric_day(Monday)
1
% # let's ask Tcl what keys are defined in the hash table
% array names numeric_day
Monday Sunday Tuesday
% # let's see if there are values for Sunday and Wednesday
% info exists numeric_day(Sunday)
1
% info exists numeric_day(Wednesday)
0
Here's a procedure that computes Fibonacci numbers in linear time by storing intermediate values in an array called fibvals. It uses the for loop, which we'll see again in the section on control structure.
proc fib {n} {
set fibvals(0) 0
set fibvals(1) 1
for {set i 2} {$i <= $n} {incr i} {
set fibvals($i) [expr $fibvals([expr {$i - 1}]) + $fibvals([expr {$i - 2}])]
}
return $fibvals($n)
}
If your index contains spaces, it will confuse the Tcl parser . For example, imagine an array called snappy_response that contains appropriate responses to various insults, which are used as the indices to the array. Suppose you want to store a response for "Have you gained weight?". You can't feed this to Tcl as
Alternatives that work:set snappy_response(Have you gained weight?) "Your mama is so fat when
she goes to beach little kids shout out 'Free Willy'!"
set snappy_response(Have\ you\ gained\ weight?) "Your mama..." set {snappy_response(Have you gained weight?)} "Your mama..." set this_insult "Have you gained weight?"
set snappy_response($this_insult) "Your mama..." % set {snappy_response(Have you gained weight?)}
Your mama is so fat when she goes to beach little kids shout out 'Free Willy'!
One of the nice things about AOLserver is that it is a single Unix process. Thus it is easy for the result of an expensive computation to be cached for later use by another thread. Here is an extremely powerful procedure that enables a programmer to cache the result of executing any Tcl statement:
This first time this procedure is called with a particular argument, the Tcl statement is evaluated (using Tcl's built-inproc memoize {tcl_statement} {
# tell AOLserver that this variable is to be shared among threads
ns_share generic_cache
# we look up the statement in the cache to see if it has already
# been eval'd. The statement itself is the key
if { ![info exists generic_cache($tcl_statement)] } {
# not in the cache already
set statement_value [eval $tcl_statement]
set generic_cache($tcl_statement) $statement_value
}
return $generic_cache($tcl_statement)
}
eval command). The result of that evaluation is then stored in the array variable generic_cache with a key consisting of the full Tcl statement. The next time memoize is called with the same argument, the info exists generic_cache($tcl_statement) will evaluate to true and the value will be returned from the cache.
Here's how a piece of code might look before:
If someone notices that (1)ns_return 200 text/html [page_with_top_10_popular_items]
page_with_top_10_popular_items requires sweeping the database and takes 30 seconds to execute, and (2) the result doesn't change more than once or twice a day, the natural conclusion is memoization: ns_return 200 text/html [memoize "page_with_top_10_popular_items"]
Our actual toollkit contains Memoize and Memoize_for_Awhile, the latter of which takes an argument of after how many seconds the information in the cache should be considered stale and reevaluated.
Typically on the Web the last thing that you'd want is an in-memory database. If the server crashes or the user gets bounced to another machine by a load-balancer, you don't want critical data to be trapped inside a Web server's virtual memory. However, there is one situation where you would want an in-memory database: to store information about the server itself.
In the ArsDigita Community System, an attempt is made to document every externally-called procedure. We want to build up a documentation database that grows as procedures are defined on a running server. The fundamental mechanism is to define procedures using our own procedure, proc_doc. This takes a documentation string as an extra argument, calls proc to actually define the procedure, then records in a Tcl array variable the file from which the procedure definition was read and the doc string:
The end-result? http://photo.net/doc/procs.tcl.proc proc_doc {name args doc_string body} {
ns_share proc_doc
ns_share proc_source_file
# let's define the procedure first
proc $name $args $body
set proc_doc($name) $doc_string
set proc_source_file($name) [info script]
}
Tcl provides support for iterating through the indices, and for coverting lists to arrays.
More: http://www.tcl.tk/man/tcl8.4/TclCmd/array.htm
If you're using AOLserver and need to associate keys with values, you might be better off using the ns_set data structure. The advantages of ns_set over Tcl arrays are the following:
See the Tcl Developer's guide at www.aolserver.com for documentation of the ns_set facility.
---
based on Tcl for Web Nerds