[KBK] closed a follow-up to [the comp.lang.tcl newsgroup] with the advice that, "Also, it's important always, always to brace your [expr]essions. 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] " [AMG]: The security problems of unbraced [expr] expressions very similar to [SQL] injection attacks. Notice how [sqlite]'s [Tcl] binding does its own variable expansion to avoid this very problem. Many, many [sh] scripts have this problem as well because the default is to apply multiple passes of interpretation. ---- [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 ---- [RS] 2007-01-09: These results puzzle me: the expressions contain no variable references, all constants. I thought the time advantage of braced expressions was because they could retrieve the numeric value of variables directly, without having to parse string reps - but in the above cases, the strings would have to be parsed either way... [AM] The numeric value of the strings is cached in the data structure that [[expr]] builds up during the parsing. So there is no real difference between numeric constants and variables with numeric values [RS] Well, but the only difference braces make, in my opinion, is that $, \, or [[...]] substitutions don't take place. If there are none, expr 1+1 should be totally equivalent (in the eyes of [expr]) to expr {1+1} [LV] It should be simple enough to enhance the above benchmark to include some variable uses to compare numeric strings vs numeric variables. - [RS] Sure, but my point was: how can "bracing" of constant strings lead to factors 0f 6..10? [LV] Ask [JH] or [AK] for certain, but I suspect that the bracing puts the strings into a cache, with a Tcl object allocated for it, so that it doesn't need to be reparsed. [NEM]: I think it is the grouping that makes the difference. With braces, [expr] gets a single argument and so can cache the parsed expression in that Tcl_Obj. Without the braces, it would have no Tcl_Obj available that represents the entire expression, so no good place to store the cache. This is a guess, but is backed up by augmenting the benchmark to also display the times for quote-grouped expressions. Add the following line to the benchmark: set time3 [lindex [time "expr \"$expression\"" 100000] 0] and adjusting the output to display this too, we get: Expression -braces +braces +quotes ratio % speedup ---------------------- -------- -------- -------- -------- ----- 1 + 1 3.290665 0.341749 0.340983 9.629:1 89.61 16 - 7 3.392816 0.337900 0.340074 10.041:1 90.04 (16 - 7) 3.486216 0.342534 0.337474 10.178:1 90.17 7 * 12 3.315694 0.339483 0.331504 9.767:1 89.76 81 / 3 3.370662 0.357784 0.347305 9.421:1 89.39 81 / 5 3.376001 0.354991 0.351884 9.510:1 89.48 16224308 + 123580329 4.481029 0.341851 0.339320 13.108:1 92.37 6.3 + 1.2 3.446636 0.349055 0.354368 9.874:1 89.87 6.3 - 1.2 3.511427 0.351007 0.346844 10.004:1 90.00 6.3 * 1.2 3.420894 0.348808 0.343441 9.807:1 89.80 6.3 / 1.2 3.471898 0.353067 0.358395 9.834:1 89.83 log(765) * 3 6.598790 0.759914 0.756337 8.684:1 88.48 (log(765) * 3) / 8.23 7.775013 0.829403 0.841776 9.374:1 89.33 So clearly the speed is mainly due to the grouping rather than braces avoiding substitition. (I also changed the braces line to ''time "expr {$expression}"'' to avoid any possible effects of pure-list optimisations, but this made negligible impact). [AMG]: This is indeed the case, as shown by '''tclCmdAH.C'''. 743 Tcl_ExprObjCmd(dummy, interp, objc, objv) 748 { 758 objPtr = Tcl_ConcatObj(objc-1, objv+1); 759 Tcl_IncrRefCount(objPtr); 760 result = Tcl_ExprObj(interp, objPtr, &resultPtr); 761 Tcl_DecrRefCount(objPtr); 768 return result; 769 } If there's only one argument to [[[expr]]], then Tcl_ConcatObj() can return that object without having to make a temporary that dies on line 761. Tcl_ExprObj() will "shimmer" the object to an expression type. If this conversion is done on the object passed by the caller (instead of on a temporary object), it will be cached inside the caller's Tcl_Obj for the next time it is passed to [[expr]]. [RS] In other words, "if you don't want to brace, don't space" :^) expr 1+1 would be faster than (the normally preferable style) expr 1 + 1 But of course, most meaningful calls to [expr] involve at least one variable reference, so the advantage of bracing isn't really challenged. ---- [[ [Arts and crafts of Tcl-Tk programming] ]]