% 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
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)".
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)
}
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
set snappy_response(Have you gained weight?) "Your mama is so fat when
she goes to beach little kids shout out 'Free Willy'!"
Alternatives that work:
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'!
proc 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)
}
This first time this procedure is called with a particular argument,
the Tcl statement is evaluated (using Tcl's built-in 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:
ns_return 200 text/html [page_with_top_10_popular_items]
If someone notices that (1) 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.
In the online community toolkit that we
built back in the 1990s, an attempt was made to document every
externally-called procedure. We wanted 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:
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]
}
The end-result? /doc/procs.tcl.
See the Tcl Developer's guide at www.aolserver.com for documentation of the ns_set facility.
Continue on to Numbers.
Another way to pass key/value pairs , especially between threads is to use nsv_set etc
-- Jamie Ross, January 12, 2005