Tcl Programming/expr
Overview
[edit | edit source]Arithmetic and logical operations (plus some string comparisons) are in Tcl concentrated in the expr command. It takes one or more arguments, evaluates them as an expression, and returns the result. The language of the expr command (also used in condition arguments of the if, for, while commands) is basically equivalent to C's expressions, with infix operators and functions. Unlike C, references to variables have to be done with $var. Examples:
set a [expr {($b + sin($c))/2.}] if {$a > $b && $b > $c} {puts "ordered"} for {set i 10} {$i >= 0} {incr i -1} {puts $i...} ;# countdown
The difference between Tcl syntax and expr syntax can be contrasted like this:
[f $x $y] ;# Tcl: embedded command f($x,$y) ;# expr: function call, comma between arguments
In another contrast to Tcl syntax, whitespace between "words" is optional (but still recommended for better readability :) And string constants must always be quoted (or braced if you wish):
if {$keyword eq "foo"} ...
Then again, Tcl commands can always be embedded into expressions, in square brackets as usual:
proc max {x y} {expr {$x>$y? $x: $y}} expr {[max $a $b] + [max $c $d]}
In expressions with numbers of mixed types, integers are coerced to doubles:
% expr 1+2. 3.0
It is important to know that division of two integers is done as integer division:
% expr 1/2 0
You get the (probably expected) floating-point division if at least one operand is double:
% expr 1/2. 0.5
If you want to evaluate a string input by the user, but always use floating-point division, just transform it, before the call to expr, by replacing "/" with "*1./" (multiply with floating-point 1. before every division):
expr [string map {/ *1./} $input]
Brace your expressions
[edit | edit source]In most cases it is safer and more efficient to pass a single braced argument to expr. Exceptions are:
- no variables or embedded commands to substitute
- operators or whole expressions to substitute
The reason is that the Tcl parser parses unbraced expressions, while expr parses that result again. This may result in success for malicious code exploits:
% set e {[file delete -force *]} % expr $e ;# will delete all files and directories % expr {$e} ;# will just return the string value of e
That braced expressions evaluate much faster than unbraced ones, can be easily tested:
% proc unbraced x {expr $x*$x} % proc braced x {expr {$x*$x}} % time {unbraced 42} 1000 197 microseconds per iteration % time {braced 42} 1000 34 microseconds per iteration
The precision of the string representation of floating-point numbers is also controlled by the tcl_precision variable. The following example returns nonzero because the second term was clipped to 12 digits in making the string representation:
% expr 1./3-[expr 1./3] 3.33288951992e-013
while this braced expression works more like expected:
% expr {1./3-[expr 1./3]} 0.0
Operators
[edit | edit source]Arithmetic, bitwise and logical operators are like in C, as is the conditional operator found in other languages (notably C):
- c?a:b -- if c is true, evaluate a, else b
The conditional operator can be used for compact functional code (note that the following example requires Tcl 8.5 so that fac() can be called inside its own definition):
% proc tcl::mathfunc::fac x {expr {$x<2? 1 : $x*fac($x-1)}} % expr fac(5) 120
Arithmetic operators
[edit | edit source]The arithmetic operators are also like those found in C:
- + addition
- - (binary: subtraction. unary: change sign)
- * multiplication
- / (integer division if both operands are integer
- % (modulo, only on integers)
- ** power (available from Tcl 8.5)
Bitwise operators
[edit | edit source]The following operators work on integers only:
- & (AND)
- | (OR)
- ^ (XOR)
- ~ (NOT)
- << shift left
- >> shift right
Logical operators
[edit | edit source]The following operators take integers (where 0 is considered false, everything else true) and return a truth value, 0 or 1:
- && (and)
- || (or)
- ! (not - unary)
Comparison operators
[edit | edit source]If operands on both side are numeric, these operators compare them as numbers. Otherwise, string comparison is done. They return a truth value, 0 (false) or 1 (true):
- == equal
- != not equal
- > greater than
- >= greater or equal than
- < less than
- <= less or equal than
As truth values are integers, you can use them as such for further computing, as the sign function demonstrates:
proc sgn x {expr {($x>0) - ($x<0)}} % sgn 42 1 % sgn -42 -1 % sgn 0 0
String operators
[edit | edit source]The following operators work on the string representation of their operands:
- eq string-equal
- ne not string-equal
Examples how "equal" and "string equal" differ:
% expr {1 == 1.0} 1 % expr {1 eq 1.0} 0
List operators
[edit | edit source]From Tcl 8.5, the following operators are also available:
- a in b - 1 if a is a member of list b, else 0
- a ni b - 1 if a is not a member of list b, else 0
Before 8.5, it's easy to write an equivalent function
proc in {list el} {expr {[lsearch -exact $list $el]>=0}}
Usage example:
if [in $keys $key] ...
which you can rewrite, once 8.5 is available wherever your work is to run, with
if {$key in $keys} ...
Functions
[edit | edit source]The following functions are built-in:
- abs(x) - absolute value
- acos(x) - arc cosine. acos(-1) = 3.14159265359 (Pi)
- asin(x) - arc sine
- atan(x) - arc tangent
- atan2(y,x)
- ceil(x) - next-highest integral value
- cos(x) - cosine
- cosh(x) - hyperbolic cosine
- double(x) - convert to floating-point number
- exp(x) - e to the x-th power. exp(1) = 2.71828182846 (Euler number, e)
- floor(x) - next-lower integral value
- fmod(x,y) - floating-point modulo
- hypot(y,x) - hypotenuse (sqrt($y*$y+$x*$x), but at higher precision)
- int(x) - convert to integer (32-bit)
- log(x) - logarithm to base e
- log10(x) - logarithm to base 10
- pow(x,y) - x to the y-th power
- rand() - random number > 0.0 and < 1.0
- round(x) - round a number to nearest integral value
- sin(x) - sine
- sinh(x) - hyperbolic sine
- sqrt(x) - square root
- srand(x) - initialize random number generation with seed x
- tan(x) - tangent
- tanh(x) - hyperbolic tangent
- wide(x) - convert to wide (64-bit) integer
Find out which functions are available with info functions:
% info functions round wide sqrt sin double log10 atan hypot rand abs acos atan2 srand sinh floor log int tanh tan asin ceil cos cosh exp pow fmod
Exporting expr functionalities
[edit | edit source]If you don't want to write [[expr {$x+5}]] every time you need a little calculation, you can easily export operators as Tcl commands:
foreach op {+ - * / %} {proc $op {a b} "expr {\$a $op \$b}"}
After that, you can call these operators like in LISP:
% + 6 7 13 % * 6 7 42
Of course, one can refine this by allowing variable arguments at least for + and *, or the single-argument case for -:
proc - {a {b ""}} {expr {$b eq ""? -$a: $a-$b}}
Similarly, expr functions can be exposed:
foreach f {sin cos tan sqrt} {proc $f x "expr {$f($x)}"}
In Tcl 8.5, the operators can be called as commands in the ::tcl::mathop namespace:
% tcl::mathop::+ 6 7 13
You can import them into the current namespace, for shorthand math commands:
% namespace import ::tcl::mathop::* % + 3 4 ;# way shorter than [expr {3 + 4}] 7 % * 6 7 42
User-defined functions
[edit | edit source]From Tcl 8.5, you can provide procs in the ::tcl::mathfunc namespace, which can then be used inside expr expressions:
% proc tcl::mathfunc::fac x {expr {$x < 2? 1: $x * fac($x-1)}} % expr fac(100) 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
This is especially useful for recursive functions, or functions whose arguments need some expr calculations:
% proc ::tcl::mathfunc::fib n {expr {$n<2? 1: fib($n-2)+fib($n-1)}} % expr fib(6) 13