** Summary ** Read and write variables ** Synopsis ** : '''set''' ''varName ?value?'' ** Documentation ** [http://www.tcl.tk/man/tcl/TclCmd/set.htm%|%set]: ** See Also ** [?set]: [expr]: [trace]: [unset]: [take]: [foreach]: often used to set multiple variables in one stroke ** Description ** A variable a value that is bound to, and accessed by a name. A variable is also bound to a particular [[`[namespace]`]. [[`set`] returns the value of variable ''varName''. ''varName'' may be any value. If ''value'' is specified, then set the value of ''varName'' to ''value'', creating a new variable if one doesn't already exist, and return its value. If ''varName'' contains an open parenthesis and ends with a close parenthesis, then it refers to an array element: the characters before the first open parenthesis are the name of the array, and the characters between the parentheses are the index within the array. Otherwise ''varName'' refers to a scalar variable. If ''varName'' is fully-qualified (begins with `::` and includes all containing [namespace]s), the variable in the specified namespace is read or written. If ''varName'' is unqualified , the following applies: If ''varName'' can be resolved to an existing variable relative to the current namespace, it resolves to that variable. Otherwise, an attempt is made to resolve ''varName'' relative to the global namespace. If that fails, ''varName'' is resolved relative to the current namespace. If no procedure is active, then ''varName'' refers to a namespace variable, which may be in the global namespace. If a procedure is active, then ''varName'' refers to a parameter or local variable of the procedure unless otherwise specified by [[`[global]`], [[`[variable]`]], or [[`[upvar]`]. [[`set`] can entirely replace [dodekalogue%|%variable expansion]. ** Variable Resolution Caveat ** [ulis] 2003-11-16: Try this: ====== set ::version 1.0 namespace eval ns { set version 0.9 } puts $::version catch {puts $ns::version} msg puts $msg ====== Result: ======none 0.9 can't read "ns::version": no such variable ====== Explanation: As stated in the Tcl manual: ''if the name does not start with a :: (i.e., is relative), Tcl follows a fixed rule for looking it up: Command and variable names are always resolved by looking first in the current namespace, and then in the global namespace. '' In the above script the variable ''version'' wasn't defined inside the namespace so Tcl used the existing global variable. To avoid that, ''always'' declare namespace variables with the '''variable''' command: ====== set ::version 1.0 namespace eval ns { variable version 0.9 } puts $::version catch { puts $ns::version } msg puts $msg ====== New result: ======none 1.0 0.9 ====== I ([ulis]) think that it would be better if the search in the global space was used when refering a variable and avoided when setting a variable. ** Setting Multiple Variables at Once ** [MSW]: For those who dislike doing multiple assignments at once with [[`[foreach]`] in the style ====== foreach {a b c} {1 2 3} {} ====== and who don't want to use [[`[lassign]`], here is a multiple argument set (with help from [RS]): ====== if {[info procs tcl::set]=={}} then {rename set tcl::set} proc set {args} { switch [llength $args] { 0 {return -code error {wrong # args: should be set varname ?newvalue? ?varname ?newvalue?? ...}} 1 {return [uplevel [list tcl::set [lindex $args 0]]]} 2 {return [uplevel [list tcl::set [lindex $args 0] [lindex $args 1]]]} default { uplevel [list tcl::set [lindex $args 0] [lindex $args 1]] return [uplevel [list set [lrange $args 2 end]]] } } } ====== Use like this ======none % set a 1 b 2 c 3 => 3 % set d 15 e [expr int(100*rand())] c => 3 % list $a $b $c $d => 1 2 3 15 ====== ---- [Duoas]: The [[`[foreach]`]..[[`[break]`] idiom is so prevalent in Tcl, and so common, that experienced Tcler's automatically recognize it as a [[`set`] replacement idiom: ====== set ls [list 1 2 3] foreach {var1 var2 ...} $ls break ====== However, something about it has always bothered me: I just dislike programming to the side-effects. I've submitted [http://www.tcl.tk/cgi-bin/tct/tip/58%|%TIP #58] to extend [[`set`] such that it can assign to multiple variables, but ''not'' as above, where the values to assign are interleaved with the variable names. Usually the values come from a list and the above implementation would require zipping variable names and values together before use, then [eval]ing or [expand]ing. It doesn't obviate the need to use that silly [foreach]..[break] idiom. Littered throughout my own code is the use of this simple little routine: ====== proc sets args { set names [lrange $args 0 end-1] set values [lindex $args end] uplevel 1 [list foreach $names $values break] return [lrange $values [llength $names] end] } ====== And an example of use: ====== sets x0 y0 x1 y1 [.canvas coords my-rectangle-tag] ====== This is much more Tclish and intuitive. Note also that you can get what is ''not'' used for later use ([foreach] requires you use it ''now'' or not at all): ====== set ls [sets a b $ls] # do something with $a and $b, and maybe sometime later with the rest of $ls ====== A more concrete example: ====== % set ls [sets a b {1 2 3 4 5}] 3 4 5 % puts $ls 3 4 5 % puts $b 2 ====== As per my TIP submission, [[`set`] is easily extended to have such functionality ''without slowing it down'' when used as per the current specification (well, except one or two processor instructions when errors occur). When used in the extended form it is faster than using [[`[foreach]`], which has a lot of extra stuff to handle multiple, concurrent lists. ---- [MJ]: in 8.5 we have [[`[lassign]`], which is [[`set`] with the arguments reversed. The example above then translates to: ======none % set ls [lassign {1 2 3 4 5} a b] 3 4 5 % puts $ls 3 4 5 % puts $b 2 ====== [Duoas]: Me feels stupid for having missed that... I learned Tcl moving into 8.0 and I'm still a little behind in a lot of 8.5 improvements. [MJ]: No need to feel stupid, Tcl 8.5 has a lot of new goodies, see [Changes in Tcl/Tk 8.5]. ** Double Indirection ** See also: [http://www.phaseit.net/claird/comp.lang.tcl/tcl_deref.html%|%An Essay on Tcl Dereferencing] In some languages, notably [PHP], an additional dollar sign can be added to a variable to achieve double-indirection. e.g. `$$var`. Tcl doesn't support such syntax, but [[`set`] can be used to the same effect: ====== % puts [set $var] ;# This works safely 5 ====== The following example, which uses [[`set`] instead of `$`, is equivalent: ====== % puts [set [set var]] ;# as does this 5 ====== Similarly, to print the values of var1, var2, and var3: ====== set var1 3.14159 set var2 hello set var3 13 foreach num {1 2 3} { puts "var$num = [set var$num]" } ====== output: ======none var1 = 3.14159 var2 = hello var3 = 13 ====== [[`[upvar]`] can also provides access to to other variables, even when they are in the same scope: ====== set var1 hello upvar 0 var1 var2 ====== [[`[eval]`] could also be used to achieve double indirection (but there are major caveats): ====== % set a 5 5 % set var a a % puts $$var ;# This doesn't work $a % eval puts $$var ;# This does - but it's dangerous 5 ====== One caveat is that if `$var` has a value containing any special characters (e.g. whitespace, semicolon), they'll get interpreted, and where this is inadvertent, could result in an error or an [Injection Attack%|%exploit]. ** An Alternative to [[`[return]`] ** [[`[proc]`] returns its last evaluated result, so it's a common [idiom] to use [[`[set]`] instead of [[`[result]`] as last command. ====== set res ====== ====== return $res ====== Some [Tcl] style guides recommend using the explicit [[`[return]`] alternative. The rationale is is that using an explicit [[`[return]`] guards against inadvertantly addint addtional code after the [[`[set]`] command. Before [[`[return]`] got byte-compiled (i.e., before Tcl 8.4), the [[`[set]`] idiom was faster than the [[`[return]`] idiom for returning a variable value. This is no-longer true. ** A Verbose [[set] ** [RS]: As Tcl has no reserved words, you can even write your own set command (make sure its effects are like the original, or most Tcl code might break). For instance, this version reports its actions on [stdout]: ====== rename set _set proc set {var args} { puts [list set $var $args] uplevel 1 _set $var $args } ====== This might help in finding what was going on before a crash. When sourced in a [wish] app, shows what's up on the Tcl side of Tk (as long as you can find the program's [stdout]). ** Misc ** [Clif Flynt]: When I'm teaching a class, I point out that ====== set set set ====== is a valid command, and that ====== set x set set y a set z b $x $y $z ====== is a valid Tcl command to assign the value b to variable a. Looking at this makes the students stop and think. And stare at me like I'm crazy. <> Tcl syntax | Arts and Crafts of Tcl-Tk Programming | Command