Tcl Programming/expr

Overview

edit

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

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

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

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

The following operators work on integers only:

  • & (AND)
  • | (OR)
  • ^ (XOR)
  • ~ (NOT)
  • << shift left
  • >> shift right

Logical operators

edit

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

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

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

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

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

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

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