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 {[lindex $args 1] eq {++}} {
set result [uplevel 1 [list ::incr [lindex $args 0 ]]]
} elseif {[lindex $args 1] eq {--}} {
set result [uplevel 1 [list ::incr [lindex $args 0] -1]]
} else {
set result [uplevel 1 [list ::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 1 $rest]
} else {
# this should always work...
set result $rest
}
if { $optional eq {@}} {
set max [llength $result]
foreach var $vars res $result {
uplevel 1 [list ::set $var $res]
}
} else {
foreach var $vars {
set result [uplevel 1 [list ::set $var $result]]
}
}
}
@ {
if {$optional eq {:}} {
set rest [uplevel 1 [list {*}$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]]
}
}
+ - - - * - / - & - | {
set script {}
foreach var $vars {
append script "
::set [list $var] \[
::expr {\[::set [list $var]] $op ($rest)}]
"
}
set result [uplevel 1 $script]
}
= -
default {
if {[catch {set result [uplevel 1 ::expr \{$rest\}]}]} {
# this should always work...
set result $rest
}
foreach var $vars {
set result [uplevel 1 [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 {} = {} exprThe 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.