A '''constant''' is a method of accessing a value such that the value never changes. ** See Also ** [math::constants]: [const package]: [male] 2007-07-26: My two cent. [Tcl and octal numbers]: [Binary representation of numbers]: [tcllib] [math::constants]: Provides some important constants. ** Description ** How do you create constants in Tcl programs? There are, of course, several ways. ---- [LV]: Well, one way to create a ''constant'' in a Tcl program is to just code it. For instance, in the line of code: ====== set a abc ====== `abc` is a constant. But what about numeric constants? A decimal value can be set thusly: ====== set c 10 ====== When `$c` is passed to a routine that expects a numeric value, the value is treated as the number '''10'''. Note that Tcl versions up through 8.4.1 treat a leading `0` as indicating that the numeric value is octal: ====== set e 010 ====== does not result in `$e` being equal to `$c`; instead, it is treated as `8`. A number can also be represented in hexadecimal: ====== % set a 0xbad 0xbad % incr a 2990 % ====== So it appears that, with appropriate notation, one can get hexadecimal. For binary, one needs to do this: ====== proc fromBinary digitString { set r 0 foreach d [split $digitString {}] { incr r $r incr r $d } return $r } puts [fromBinary 0101001010101010] puts [format %x [fromBinary 0101001010101010]] ====== or ====== proc bits2int bits { #returns integer equivalent of a bitlist set bits [format %032s [join $bits {}]] binary scan [binary format B* $bits] I1 x set x } ====== ---- There are various ways to inhibit change to the value of a variable or to access a value in some way designed to prevent modification of the value: ---- '''procedure constants''' Create a procedure for each constant, which returns the value. ====== proc FLAG1 {} { return 0x0001 } ====== You then access the constant by calling the procedure, like `FLAG1`. This is easy to code, but gets clumsy when you have lots of constants. Another possibility is to use an array in one constant function, but this is fairly inflexible. ====== proc CONST key { array set constant { FLAG1 0x001 FLAG2 0x002 PI 3.14159 } return $constant($key) } ====== ---- '''readonly trace''' [Brent Welch] suggests using write variable [trace%|%traces] to implement read-only variables. But since the write trace fires ''after'' the variable value has changed, you need to keep a cache of the original value somewhere. [George Howlett]: made a suggestion (at a Tcl conference tutorial) that read traces are your friend. He suggested a couple of very flexible procedures. (According to [Don Porter]'s c.l.t. post) ====== proc _constant_read_trace {val name1 name2 ops} { upvar $name1 var set var $val } proc constant {varName value} { uplevel [list trace variable $varName r [list _constant_read_trace $value]] } ====== '''example:''' ====== % constant PI 3.14159 % set PI 3.14159 % set PI 3; # Only in Indiana :) 3 % set PI 3.14159 ====== ---- However, note the following: ====== % set v [set PI 3] 3 % puts $v 3 ====== Thus, while PI continues to have a constant value, note that the result from the set ''appears'' as if PI had been given a different value. So one needs to be careful how one plans on using this code. ---- [RS]: Slightly modified the above, so attempts to vary a constant raise an error (it probably was one ;-): ====== proc _constant_trace {val name1 name2 ops} { upvar $name1 var if {$ops eq {w}} { return -code error "constant $val may not be changed" } set var $val } proc constant {varName value} { uplevel [list trace variable $varName rw [list _constant_trace $value]] } ====== '''example:''' ======none % set PI 1.23 can't set "PI": constant 3.14159 may not be changed ====== ---- [RS] again: This raises no error, but keeps a constant with minimal code: ====== proc const {name value} { uplevel 1 [list set $name $value] uplevel 1 [list trace var $name w "set [list $name] [list $value];#"] } ====== Simple error-raising variation: ====== proc const {name value} { uplevel 1 [list set $name $value] uplevel 1 [list trace var $name w {error constant ;#} ] } % const x 11 % incr x can't set "x": constant ====== ---- [kruzalex]: This prevents constant without raising an error too: ====== rename set _set proc set {var args} { if {[llength $args]!=0} { uplevel 1 [list _set $var $args] } else { return [uplevel 1 [list _set $var]] } } proc _constant_read_trace {val name1 name2 ops} { upvar $name1 var uplevel 1 [list _set $name1 $val] } proc constant {varName value} { uplevel [list trace variable $varName r [list _constant_read_trace $value]] } constant PI 3.14159 puts #step1 puts [set PI] set PI 3; # Only in Indiana :) puts #step2 puts [set PI] puts #step3 set some [set PI 3] puts [set PI] puts "some: $some" puts #step4 set some 4 puts "some: $some" puts #step5 set some [set PI] puts "some: $some" ====== ---- [RS]: From a [comp.lang.tcl] post, this variation is not about preventing changes, but to import defined "constants" in [proc] scope: ====== interp alias {} define {} lappend ::defines proc use_defines {} { foreach {key val} $::defines {uplevel 1 [list set $key $val]} } #-- Test and demo: define PI 3.14 define e 2.781 proc try {} { use_defines return "PI=$PI, e=$e" } % try PI=3.14, e=2.781 ====== ---- [Karl Lehenbauer], I think, gets credit for one invention of read-only variables [[cite references]]. ---- ====== #By George Peter Staplin set ::constants [list] proc constant {name value} { global constants lappend constants $name $value } proc constproc {name argpat body} { global constants proc $name $argpat [string map $constants $body] } constant PI 3.14159 constant PROCESS [pid] constant FLAG1 2 constant FLAG2 4 constant FLAG3 8 proc & {a b} { expr {$a & $b} } constproc test {} { puts "PI PROCESS" set n 107 puts "[& $n FLAG1] [& $n FLAG2] [& $n FLAG3]" } test ====== ---- [NEM] notes that command names are the natural choice for constants: * they live in a separate namespace to variables; * they are rarely redefined, and few commands do so; * global commands are available everywhere without importing; * they offer the possibility of byte-code optimisation, i.e. inlining (no idea if this is or could be done). ====== proc def {name = args} { interp alias {} $name {} const [expr $args] } proc const a { return $a } def PI = acos(-1) def PROCESS = [pid] def FLAG1 = 0x01 def FLAG2 = 0x02 def FLAG3 = [FLAG1] | [FLAG2] proc test {} { puts "[PI] [PROCESS]" puts "FLAG3 = [FLAG3]" } test ====== ---- [wdb]: Good idea ... when making a function, why not making it even more handy? ====== proc pi args [subst -novariable { expr [expr {atan2(0,-1)}] $args }] ====== Now, use it: ====== % pi 3.14159265359 % pi / 2 1.5707963268 ====== ---- [Stu] 2011-01-11: On similar lines, I came up with this recently. ====== proc aliasconst {name val args} { if {[llength $args] % 2 != 0} { error "must be %2!" } foreach {n v} [linsert $args 0 $name $val] { interp alias {} $n {} return -level 0 $v } } % aliasconst ^Z \x1a ^J \n me Stu % me Stu ====== ** Page Authors ** [PYK]: <> Concept