[Richard Suchenwirth] 2002-05-05 - Static variables are seen here as variables that are local to a procedure, but retain their value ("state") between calls to the procedure. One way to maintain a variable's state is to make it global (which includes putting it into a namespace), but this is frowned upon as it clutters the environment. Another way, as shown in ''intgen'' in [Braintwisters], is to store the value as default for a proc argument, and rewrite the proc definition. To generalize the specific approach taken there, we first write a proc that rewrites one argument's default with a given value: proc redefaultProc {procn argn value} { set argl {} foreach arg [info args $procn] { if [info default $procn $arg default] { if {$arg==$argn} {set default $value} lappend argl [list $arg $default] } else { lappend argl $arg } } proc $procn $argl [info body $procn] } if 0 {With this tool in hand, we can proceed to write ''static'', a command that somehow resembles a C "declaration", but actively registers arguments as "static", i.e. installs a write trace so every time the variable is changed, the proc definition is rewritten (for performance, don't assign to it too often!):} proc static args { set caller [lindex [info level -1] 0] foreach arg $args { uplevel 1 [list trace var $arg w [list staticTrace $caller]] } } # ... and the trace proc, which only serves to ignore 'el' and 'op': proc staticTrace {caller name el op} { upvar 1 $name var redefaultProc $caller $name $var } if 0 {..and now testing the whole thing. Note that a ''static'' variable needs also to be written into the proc argument list with a default value to start with: proc x {y {z 0}} { static z puts [incr z]:$y } % x hello 1:hello % x world 2:world Looks like we made it - proc x has a "counter" z that remembers how often it was called before, but you can reset it explicitly (as the call to x pre-increments, use -1 to start next time with a 0 counter): % x reset -1 0:reset % x test 1:test Static variables need not be numbers, for instance this example shows that it always remembers the previous call's argument (any string): proc lookback {now {before {}}} { static before puts $before->$now set before $now } And ''intgen'' can now be rewritten like this: proc intgen {{seed 0}} {static seed; incr seed} ---- [Category Concept] | [Arts and crafts of Tcl-Tk programming]