Array Operations

part of Tcl for Web Nerds by Hal Abelson, Philip Greenspun, and Lydia Sandon; updated July 2011
Tcl arrays are actually hash tables and have nothing in common with the data structures called arrays in other programming languages . A Tcl array provides a rapid answer to the question "is there a value associated with this key". Here is a rat-simple example:
% 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)".

You Can Use Tcl Array with Numbers as Keys

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)
}

Dealing with spaces inside your keys

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
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 is so fat when she goes to beach little kids shout out 'Free Willy'!

How We Actually Use Tcl Arrays: Caching

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:
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.

How We Actually Use Tcl Arrays: In-Memory Database

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 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.

Full Documentation

Tcl provides support for iterating through the indices, and for coverting lists to arrays. These are documented in http://www.tcl.tk/man/tcl8.4/TclCmd/array.htm

ns_set instead

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: A disadvantage of ns_sets is that they require O[n] time to look up the value of key, compared to O[1} time for the Tcl array, which, as noted above, is actually a hash table. If you are managing thousands of keys, this might become significant. Otherwise, program using whichever data structure seems more natural.

See the Tcl Developer's guide at www.aolserver.com for documentation of the ns_set facility.



Continue on to Numbers.


Return to Table of Contents

lsandon@alum.mit.edu

Reader's Comments

Another way to pass key/value pairs , especially between threads is to use nsv_set etc

-- Jamie Ross, January 12, 2005
Add a comment | Add a link