Monday, November 17, 2008

More about Variables

The set command will return the value of a variable if it is only passed a single
argument. It treats that argument as a variable name and returns the current
value of the variable. The dollar-sign syntax used to get the value of a variable is
really just an easy way to use the set command. Example 1–15 shows a trick you
can play by putting the name of one variable into another variable:
* Ironically, Tcl 8.0 introduced a byte-code compiler, and the first releases of Tcl 8.0 had a bug in the compiler
that caused this loop to terminate! This bug is fixed in the 8.0.5 patch release.
14 Tcl Fundamentals Chap. 1

Example 1–15 Using set to return a variable value.
set var {the value of var}
=> the value of var
set name var
=> var
set name
=> var
set $name
=> the value of var
This is a somewhat tricky example. In the last command, $name gets substituted
with var. Then, the set command returns the value of var, which is the
value of var. Nested set commands provide another way to achieve a level of
indirection. The last set command above can be written as follows:
set [set name]
=> the value of var
Using a variable to store the name of another variable may seem overly
complex. However, there are some times when it is very useful. There is even a
special command, upvar, that makes this sort of trick easier. The upvar command
is described in detail in Chapter 7.
Funny Variable Names
The Tcl interpreter makes some assumptions about variable names that
make it easy to embed variable references into other strings. By default, it
assumes that variable names contain only letters, digits, and the underscore.
The construct $foo.o represents a concatenation of the value of foo and the literal
“.o”.
If the variable reference is not delimited by punctuation or white space,
then you can use curly braces to explicitly delimit the variable name (e.g., ${x}).
You can also use this to reference variables with funny characters in their name,
although you probably do not want variables named like that. If you find yourself
using funny variable names, or computing the names of variables, then you may
want to use the upvar command.

Example 1–16 Embedded variable references.
set foo filename
set object $foo.o
=> filename.o
set a AAA
set b abc${a}def
=> abcAAAdef
set .o yuk!
set x ${.o}y
=> yuk!y
More about Math Expressions 15
I. Tcl Basics
The unset Command
You can delete a variable with the unset command:
unset varName varName2 ...
Any number of variable names can be passed to the unset command. However,
unset will raise an error if a variable is not already defined.
Using info to Find Out about Variables
The existence of a variable can be tested with the info exists command.
For example, because incr requires that a variable exist, you might have to test
for the existence of the variable first.
Example 1–17 Using info to determine if a variable exists.
if {![info exists foobar]} {
set foobar 0
} else {
incr foobar
}
Example 7–6 on page 86 implements a new version of incr which handles this
case.
More about Math Expressions
This section describes a few fine points about math in Tcl scripts. In Tcl 7.6 and
earlier versions math is not that efficient because of conversions between strings
and numbers. The expr command must convert its arguments from strings to
numbers. It then does all its computations with double precision floating point
values. The result is formatted into a string that has, by default, 12 significant
digits. This number can be changed by setting the tcl_precision variable to the
number of significant digits desired. Seventeen digits of precision are enough to
ensure that no information is lost when converting back and forth between a
string and an IEEE double precision number:
Example 1–18 Controlling precision with tcl_precision.
expr 1 / 3
=> 0
expr 1 / 3.0
=> 0.333333333333
set tcl_precision 17
=> 17
expr 1 / 3.0
# The trailing 1 is the IEEE rounding digit
=> 0.33333333333333331
16 Tcl Fundamentals Chap. 1
In Tcl 8.0 and later versions, the overhead of conversions is eliminated in
most cases by the built-in compiler. Even so, Tcl was not designed to support
math-intensive applications. You may want to implement math-intensive code in
a compiled language and register the function as a Tcl command as described in
Chapter 44.
There is support for string comparisons by expr, so you can test string values
in if statements. You must use quotes so that expr knows to do string comparisons:
if {$answer == "yes"} { ... }
However, the string compare and string equal commands described in
Chapter 4 are more reliable because expr may do conversions on strings that
look like numbers. The issues with string operations and expr are discussed on
page 48.
Expressions can include variable and command substitutions and still be
grouped with curly braces. This is because an argument to expr is subject to two
rounds of substitution: one by the Tcl interpreter, and a second by expr itself.
Ordinarily this is not a problem because math values do not contain the characters
that are special to the Tcl interpreter. The second round of substitutions is
needed to support commands like while and if that use the expression evaluator
internally.
Grouping expressions can make them run more efficiently.
You should always group expressions in curly braces and let expr do command
and variable substitutions. Otherwise, your values may suffer extra conversions
from numbers to strings and back to numbers. Not only is this process
slow, but the conversions can loose precision in certain circumstances. For example,
suppose x is computed from a math function:
set x [expr {sqrt(2.0)}]
At this point the value of x is a double-precision floating point value, just as
you would expect. If you do this:
set two [expr $x * $x]
then you may or may not get 2.0 as the result! This is because Tcl will substitute
$x and expr will concatenate all its arguments into one string, and then parse
the expression again. In contrast, if you do this:
set two [expr {$x * $x}]
then expr will do the substitutions, and it will be careful to preserve the floating
point value of x. The expression will be more accurate and run more efficiently
because no string conversions will be done. The story behind Tcl values is
described in more detail in Chapter 44 on C programming and Tcl.
Comments
Tcl uses the pound character, #, for comments. Unlike in many other languages,
the # must occur at the beginning of a command. A # that occurs elsewhere is not
treated specially. An easy trick to append a comment to the end of a command is
Substitution and Grouping Summary 17
I. Tcl Basics
to precede the # with a semicolon to terminate the previous command:
# Here are some parameters
set rate 7.0 ;# The interest rate
set months 60 ;# The loan term
One subtle effect to watch for is that a backslash effectively continues a
comment line onto the next line of the script. In addition, a semicolon inside a
comment is not significant. Only a newline terminates comments:
# Here is the start of a Tcl comment \
and some more of it; still in the comment
The behavior of a backslash in comments is pretty obscure, but it can be
exploited as shown in Example 2–3 on page 27.
A surprising property of Tcl comments is that curly braces inside comments
are still counted for the purposes of finding matching brackets. I think the motivation
for this mis-feature was to keep the original Tcl parser simpler. However,
it means that the following will not work as expected to comment out an alternate
version of an if expression:
# if {boolean expression1} {
if {boolean expression2} {
some commands
}
The previous sequence results in an extra left curly brace, and probably a
complaint about a missing close brace at the end of your script! A technique I use
to comment out large chunks of code is to put the code inside an if block that
will never execute:
if {0} {
unused code here
}