Version 5 of Brace your expr-essions

Updated 2005-12-10 12:48:55

KBK closed a follow-up to the comp.lang.tcl newsgroup with the advice that, "Also, it's important always, always to brace your expressions. Don't do

     set a [expr $b / $c]

but rather

     set a [expr { $b / $c }]

Otherwise, your variables may undergo unexpected conversions numeric -> string -> numeric, you can lose precision, your expressions will be much slower, and you can even have security problems:

     # don't run
     set x { [format C:\] }
     set y [expr $x + 3]

"


RS: In the following cases braced expressions don't work:

  • operator in variable (which in most languages is impossible, but Tcl can do):
 set op +
 expr $x $op $y
  • (parts of) expression generated by string manipulation:
 expr [join $intlist +]

KPV: Braced expressions also don't work when you do negation in the lazy way:

  set n1 -3
  set n2 -$n1
  expr {2 + $n2} ;# error
  expr 2 + $n2   ;# ok

aricb Dec. 10 2005 -- As a bonus, braced expressions are generally much faster than their non-braced counterparts. Here's an attempt to illustrate the difference:

 proc time_braces {} {

     set expressions {
         {1 + 1}
         {16 - 7}
         {(16 - 7)}
         {7 * 12}
         {81 / 3}
         {81 / 5}
         {16224308 + 123580329}
         {6.3 + 1.2}
         {6.3 - 1.2}
         {6.3 * 1.2}
         {6.3 / 1.2}
         {log(765) * 3}
         {(log(765) * 3) / 8.23}
     }

     puts [format " %-22s  %-8s  %-8s  %-8s  %-5s" "Expression" "-braces" "+braces" "ratio" "% speedup"]
     puts " [string repeat - 22]  [string repeat - 8]  [string repeat - 8]  [string repeat - 8]  [string repeat - 5]"

     foreach expression $expressions {
         set time1 [lindex [time "expr $expression" 100000] 0]
         set time2 [lindex [time [list expr $expression] 100000] 0]
         set ratio [expr {$time1 / $time2}]
         set percent [expr {100 * (1 - ($time2 / $time1))}]
         puts [format " %-22s  %8.6f  %8.6f  %6.3f:1  %5.2f" $expression $time1 $time2 $ratio $percent]
     }

     puts "\n -braces and +braces are in microseconds per iteration"
 }

Results:

 Expression              -braces   +braces   ratio     % speedup
 ----------------------  --------  --------  --------  -----
 1 + 1                   2.334690  0.366480   6.371:1  84.30
 16 - 7                  2.388280  0.359960   6.635:1  84.93
 (16 - 7)                3.748160  0.356120  10.525:1  90.50
 7 * 12                  2.438530  0.368200   6.623:1  84.90
 81 / 3                  2.456060  0.395680   6.207:1  83.89
 81 / 5                  2.518490  0.397380   6.338:1  84.22
 16224308 + 123580329    2.726690  0.369530   7.379:1  86.45
 6.3 + 1.2               3.237970  0.497100   6.514:1  84.65
 6.3 - 1.2               3.199230  0.486350   6.578:1  84.80
 6.3 * 1.2               3.279100  0.480630   6.823:1  85.34
 6.3 / 1.2               3.257540  0.494340   6.590:1  84.82
 log(765) * 3            6.202120  0.975590   6.357:1  84.27
 (log(765) * 3) / 8.23   7.951270  1.142210   6.961:1  85.63

 -braces and +braces are in microseconds per iteration