87.14%
Search · Index

## V.5 Array Operations

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) 00% set numeric_day(Monday) 11% set numeric_day(Tuesday) 22% # 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_dayMonday 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 whenshe goes to beach little kids shout out 'Free Willy'!"`
Alternatives that work:
• Escape all the spaces with backslash:
`set snappy_response(Have\ you\ gained\ weight?) "Your mama..."`
• Enclose the array name and parenthesized key in curly braces:
`set {snappy_response(Have you gained weight?)} "Your mama..."`
• Name the index with a variable and then use the variable:
`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'!`

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

``` ```
`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? http://photo.net/doc/procs.tcl.

### Full Documentation

Tcl provides support for iterating through the indices, and for coverting lists to arrays.