Toggling a Boolean Variable

proc toggle {v} {
    uplevel [concat set $v \[expr \[set $v\] ^ 1\]]
}

% set state 0
0
% toggle state
1
% toggle state
0

RS has this alternative:

proc toggle varName {
     upvar 1 $varName var
     set var [expr {!$var}]
}

Using the not operator (!) has, apart from being straightforward, the advantage that it also accepts the other allowed forms for truth values (yes, no, on, off, true, false). Also, as every non-zero int or double implies 'true' for expr, the other alternatives may lead to subtle bugs.

Not on tcltk 8.3.2, where the ! operator to expr gives me

  can't use non-numeric string as operand of "!"

That's why KPV's version is #1 for me. --Ro

escargo has this alternative:

proc toggle varName {
     upvar 1 $varName var
     set var [expr {1 - $var}]
}

This is a clearly inferior alternative (probably on the basis of performance, but also because of the subtle bugs), but I wanted to include it for completeness. In fact, I found a bug in a Fortran compiler once where code optimization turned the expression

    TOGGLE = 1 - TOGGLE

into a decrement instruction. That made my Fortran program misbehave in mysterious ways until I figured out what was going wrong.

KPV has yet another alternative

proc toggle varName {
    upvar 1 $varName var
    set var [expr {$var ? 0 : 1}]
}

KPV not quite the same thing, but I've liked the following idiom for normalizing boolean values, i.e. converting all true values into 1, and all false values into 0:

set b [expr {!!$b}]

GPS: when I started this page I should have thought about my code more. The lack of using upvar was a major mistake. I wrote a little script to compare the various solutions.

catch {console show}

proc gps_orig_toggle {v} {
    uplevel [concat set $v \[expr \[set $v\] ^ 1\]]
}

proc gps_toggle {vPtr} {
    upvar 1 $vPtr v
    set v [expr {$v ^ 1}]
}

proc rs_toggle {varName} {
    upvar 1 $varName var
    set var [expr {!$var}]
}

proc esc_toggle {varName} {
    upvar 1 $varName var
    set var [expr {1 - $var}]
}

proc kpv_toggle {varName} {
    upvar 1 $varName var
    set var [expr {$var ? 0 : 1}]
}

proc main {} {
    set gps_orig_v 0
    puts GPSORIG_[time {gps_orig_toggle gps_orig_v} 9000]

    set gps_v 0
    puts GPS_[time {gps_toggle gps_v} 9000]
    set rs_v 0
    puts RS_[time {rs_toggle rs_v} 9000]
    set esc_v 0
    puts ESC_[time {esc_toggle esc_v} 9000]
    set KPV_v 0
    puts KPV_[time {kpv_toggle kpv_v} 9000]
}
main

I've run the tests many times in Windows XP, and the results for the last three (now four) tend to be identical or vary by 1 microsecond per iteration. There doesn't seem to be a clear winner. KPV Same is true for win2k.

There is a clear winner, and it's KPV's version:

proc kpv_toggle {varName} {
    upvar 1 $varName var
    set var [expr {$var ? 0 : 1}]
}

since this is the only one that works with all of tcl's wonderful boolean string variables, like true, false, yes and no. At least this is on tcltk 8.3.2 on my win98 (first ed). --Ro


16feb04 jcw - Other ways to toggle 0/1's:

set a [regexp 0 $a]
set a [string match 0 $a]
set a [string map {0 1 1 0} $a]

SS 16feb2004 - Another alternative is to let if to provide the way to check the current state.

proc ifbased_toggle varname {
    upvar 1 $varname var
    set var [if {$var} {format 0} {format 1}]
}

MG offers another alternative on March 3rd 2005 (which only works with 1/0, not yes/no, true/false, etc)...

proc mg_toggle varName {
    upvar 1 $varName var
    set var [lindex "1 0" $var]
}

Seems to come up slightly slower (about 1-2 microseconds) than the others above, though.


Jasp would usually use expr, but notes you could also use:

proc jaspToggle {varName} {
     upvar 1 $varName variable
     set variable [string is false -strict $variable]
}