Foreach Multiset
Problem
How can one efficiently set the value of multiple variables?
Context
Setting multiple variables using a sequence of set statements
causes noticeable performance problems in tight loops but the script cannot
be reimplemented in C.
Forces
- The application must run quickly.
- The performance improvements must be implemented at the script level, perhaps to allow code to be downloaded safely or to avoid the slow edit/compile/link/test cycle required when writing C extensions.
Solution
The foreach command can be used to set multiple variables with
a single call. If fewer variables than list elements are passed to the
command, these variables are changed each time round the loop. However,
if the foreach command is invoked with the same number of
variables and list elements, and with an empty loop body, the variables
are set to the values stored in the list.
Consequences
Multiple variables can be efficiently set with a single command invocation.
The code is obscure if you don't know the idiom. (That's what these pages are for :-)
Known Uses
This idiom was suggested by Ross J.Reedstrom. It is used in code by Jeff Hobbes (check out his tcl pages), and has also been spotted in code by the Sun Tcl Team.
Example Code
The following command sets the variable var1 to the string
value1, the variable var2 to the string
value2 and the variable var3 to the string
value3.
foreach {var1 var2 var3} {value1 value2 value3} {}
|
This technique can be used to extract elements from a tuple (list) returned from a command, as shown below.
foreach {x y z} [$a_vrml_object getPosition] {}
|
If the proc might return an empty list, the code above would fail. You can make the code safe, at the expense of an additional iteration, by prepending default values to the list returned by the command:
foreach {x y z} [concat 0 0 0 [$a_vrml_object getPosition]] {}
|
If the command might return more elements than you need and you only
want to process the first few, you can use a break
statement as the body of the foreach loop to ensure
that the command only performs a single iteration and so doesn't
overwrite the variables with the later elements of the list. For
example, the code below extracts the red, green and blue components
from a tuple specifying a colour; if the operation was extended to
return the alpha (transparency) value as the fourth element of the
tuple, the code would still work as expected.
foreach {r g b} [$a_vrml_object getColour] break
|
Obviously, this technique cannot be combined with the use of default values.
Suggested in postings to the comp.lang.tcl newsgroup by Ross J. Reedstrom and Jeff Hobbs.