Let's assign with let presents techniques to assign data to multiple variables. For example, one might transpose the value of two variables.
proc let { args } { if { [llength $args ] == 2 } { if [string equal [ lindex $args 1 ] "++" ] { set result [ uplevel incr [ lindex $args 0 ] ] } elseif [string equal [ lindex $args 1 ] "--" ] { set result [ uplevel incr [ lindex $args 0 ] -1 ] } else { set result [ uplevel set "$args" ] } } else { regexp {([^=:+\-*/&|@]*)([:+\-*/&|@]?)([@]*)=(.*)} $args -> vars op optional rest if ![ info exists op ] { return -code error -errorcode 1 "no valid assignment operator in $args" } switch -- $op { : { if [llength [info commands [lindex $rest 0]]] { set result [uplevel $rest] } else { set result $rest ;# this should always work... } if { "$optional" == "@" } { set max [ llength $result ] foreach var $vars res $result { uplevel 1 [ list set $var $res ] } } else { foreach var $vars { set result [ uplevel [list set $var $result] ] } } } @ { if { "$optional" == ":" } { set rest [uplevel $rest] } set max [ llength $rest ] if { $max == 1 } { eval set rest $rest set max [ llength $rest ] } foreach var $vars res $rest { set result [ uplevel 1 [ list set $var $res ]] } } + - - - * - / - & - | { foreach var $vars { set result [ uplevel set $var \[ expr \$$var $op ( $rest ) \] ] } } = - default { if { [ catch { set result [ uplevel expr $rest ] } ] } { set result $rest ;# this should always work... } foreach var $vars { set result [ uplevel [list set $var $result] ] } } } } return $result }
Example: Transposing two variables:
let a b @= $b $a
Which, by the way, also allows:
let a b c = 1 ;#this sets a b and c to 1 let a b c = 1 + 4 ;#"=" uses expr to process the value to assign let a b c += 1 ;#computed assignments are allowed, +-*/&| supported let a b c := info commands ;#uses eval to process right side let a b c @= 1 2 3 ;#instead of assigning the list {1 2 3} to a b and c, ;#it instead assigns the elements in order, resulting ;#in a getting the value 1, b getting 2 and so on. let a b c @:= info commands ;#uses eval to get result and uses @= for assignment let a ++ ;#incr and let a -- ;#decr are supported.
RS The code fragment (appears twice above)
set i 0 foreach var $vars { uplevel set $var [ lindex $result $i ] incr i }
seems like it can be replaced by
foreach var $vars res $result { uplevel 1 [list set $var $res] }
- if foreach can do a job once, it might as well do it twice ;-) A variable saved, and possibly safer with the list wrapper...
Larry Smith: I'd clean forgotten that feature. Yes, it's much more elegant that way. The above code has been corrected.
My suggestion will swap a-b and vice versa:
foreach {a b} [list $b $a] {}
Although let has many other features of course.
aspect 2015: For interactive use I like to have the following two definitions active (from .tclshrc):
# let <varName> <cmd> <arg>.... proc let {name args} { tailcall ::set $name [uplevel 1 $args] } interp alias {} = {} expr
The mostly saves me having to contort my fingers to write expr { and }. The specific combination using = as a pun for expr I find more Tclish than = as an always-present keyword argument.
let pi = 4*atan(1)
Of course the resulting code is suboptimal and dangerous (Brace your expressions!), but it is a lot more fun to write. The only problem is that before publication I need to clean up, but that's a few editor macros away.
You will find some of the toys I paste to the wiki use this combination, where I feel readability trumps robustness.