Indirect References

A powerful programming construct is the use of common procedures that operate on a data structure of a particular type. In C, you might have a set of procedures to manipulate a struct; procedures are coded to accept pointers to the actual struct, so you pass a pointer to any number of structures to the procedures.

Tcl doesn’t have a struct data type, but arrays indexed by elements are a close approximation. For example, you might have data on states of the United States and a procedure to calculate population density:

set mo(name) Missouri
set mo(pop) 5402058.0
set mo(area) 68945.0

set co(name) Colorado
set co(pop) 3892644.0
set co(area) 103598.0

How then to reference a specific state array based on the name of one of the arrays? The first instinct is to try to use two $ characters to deference the variable:

foreach aState [list mo co] {
    puts "State Name: $$aState(name)"
}

Tcl’s parsing rules state that variable substitution is performed exactly once for each command, so the command fails, leaving an invalid variable name $pmo(name), rather than mo(name).

The first way to deal with this situation is using a nested set command. Set without a third argument returns the current value of the variable. The variable $aState is first expanded by Tcl, leaving the correct variable name mo(name) for set to return its value:

set aState mo
puts "State Name: [set ${aState}(name)]"

This code produces the following output:

State Name: Missouri

Note that we must also use braces around aState; otherwise, the Tcl parser will think we are trying to reference an array element aState(name), which doesn’t exist.

Tcl’s upvar command is another answer to coding indirect variable references. Upvar allows one to reference a variable or array by some other name. Using a first argument of 0 allows variables in the current scope to be accessed.

foreach state_array_name [list mo co] {
    upvar 0 $state_array_name aState
    set p [expr $aState(pop) / $aState(area)]
    puts "$aState(name) has a population density of $p"
}

This code produces the following output:

Missouri has a population density of 78.3531510624
Colorado has a population density of 37.5745091604

Upvar is also used when passing arrays to procedures, in which the default procedure scope frame (1) is used:

proc calc_pop_density {state_array_name} {
    upvar $state_array_name aState
    set p [expr $aState(pop) / $aState(area)]
    puts "$aState(name) has a population density of $p"
}
calc_pop_density mo

This code produces the following output:

Missouri has a population density of 78.3531510624

Sometimes the solution is to rethink your particular implementation. Lists can be used in many places where arrays can be used, and Extended Tcl’s keyed list commands also provide struct-like data types. Multidimensional arrays can also be simulated in Tcl.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset