Version 11 of Toggling a Boolean Variable

Updated 2002-11-07 19:16:31

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 its 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


Category Example