Version 6 of Variable Substitution

Updated 2003-08-31 18:23:46

ulis, 2003-08-31


Related pages:


Variable substitution is the way Tcl gets a value from a name:

  set v aValue
  puts $v

The $v notation tells Tcl to get the value of the variable v (here the value is "aValue").

The $ notation was added to Tcl to simplify the codification and is not needed. The set command (with one argument) returns the value of a variable. And the two following lines are equivalent:

  puts $v
  puts [set v]

Sometimes the $ notation can't be used without difficulty. This is when the name of the variable is composed from other variables:

  foreach i {1 2 3} { set var$i value$i }
  foreach i {1 2 3} { puts $var$i } <-- this fails (var is undefined)

TV I fails, logically speaking, because you are trying to do two evaluation steps in a certain order while your syntax puts them on the same level. What you want is to evaluate what the string concatenation of 'var' with the content of variable i becomes, and get the content of the variable with the resulting name. Not in hell, and not unlogically, though possibly prone to syntactical issues (like special characters), you could rightfully and reasonable write:

   foreach i {1 2 3} { eval puts "\$var$i" }

without the extra 'eval', which evaluates its argument list once, you'd get the logical and desirable intermedeate result:

   foreach i {1 2 3} { puts "\$var$i" }
      $var1
      $var2
      $var3

  foreach i {1 2 3} { set var$i value$i }
  foreach i {1 2 3} { puts $var$i } <-- this fails (var is undefined)

The last line fails because Tcl tries to substitutes $var and then $i and finds that var is not defined.

The right solution is to use set:

  foreach i {1 2 3} { puts [set var$i] }

Here Tcl substitutes the value of i and then set returns the value of var1, var2, var3.

To best understand what appends when Tcl is substituing variables let define a $ alias that does the $ substitution:

  interp alias {} $ {} set

Replacing the $ notation by a $ call, the following lines are equivalent two by two:

  foreach i {1 2 3} { puts $var$i } <-- this fails (var is undefined)
  foreach i {1 2 3} { puts [$ var][$ i] } <-- this fails (var is undefined)

  foreach i {1 2 3} { puts [set var$i] }
  foreach i {1 2 3} { puts [$ var[$ i]] }

Composing the name of a variable is a clear signal that an array could be used:

  foreach i {1 2 3} { set var($i) value$i }
  foreach i {1 2 3} { puts $var($i) }

The last line is equivalent to:

  foreach i {1 2 3} { puts [$ var([$ i])] }

TV It could also indicate an indirection, an important programming concept, and in some form a major language discussion issue in the history of tcl/tk:

   set a something
   set pointer a
   eval puts "\$$pointer"

ulis The last line is equivalent to:

  puts [$ [$ pointer]]

And can be rewritten, avoiding eval:

  puts [set $p]