Tcl Math: Using Expresions

Math is performed using the expr command. The expr command is used to evaluate expressions and plays by its own set of rules. The syntax for the command is

expr argument1 argument2 ... argumentN 

expr is different than most commands because it interprets numbers as numbers instead of strings, booleans as booleans, allows for mathematical operators, and requires literal strings to be wrapped in quotes. Another crucial difference is that expr performs its own round of substitution apart from the Tcl compiler. We’ll talk more about that later but just know it can cause problems if you’re not careful. There are three ways of passing arguments to the expr command

expr 5+5
expr "5+5"
expr {5+5}

Best practice is to use the last example where the arguments are wrapped in braces. This is for two reasons first, it is faster. Second, and perhaps more importantly, it prevents the Tcl compiler from performing the first round of substitution and leaves it up to the expr command to perform substitutions. This avoids double substitution (double because the Tcl compiler performs substitution first and then the expr command performs substitution again). Check out the following example

set x 8
set y x
set z y

expr {$x+3}
expr {$$y+3}; #this command fails
expr $$y+3; #this command executes
expr $$$z+3; #this command fails

If you tried to run the above code you would have gotten an error when you hit the line expr {$$y+3}. Remember, the braces prevent the Tcl compiler from performing the first round of substitution so expr gets the following expression to evaluate:  $x+3. There are two problems with this, first  $ is a string and not wrapped in quotes. Second, expr doesn’t know how to evaluate $x+3 resulting in an error. If you comment out that line by adding the # character at the begining of the line like so, #expr {$$y+3}, and move to expr $$y+3 you’ll find that the command executes properly. What’s going on here? The Tcl compiler performs the first round of substitution so the command looks like expr $x+3. Then expr performs its substitution rendering 8+3 which is evaluated to 11. This is called double substitution and in general is something you want to avoid. As stated earlier it is slower but it also makes your code vulnerable to certain attacks. For more information, I refer you to https://wiki.tcl-lang.org/page/Injection+Attack. The last line expr $$$z+3 will also cause an error because after the 2 rounds of substitution expr  tries to evaluate $x+3. We already know why that fails.

The previous examples using expr evaluated expressions but the result was not captured or displayed. It was simply lost. To capture and store the result of expr you must use the set command as shown

set x [expr {5+5}]; #notice the brackets around the expr command
puts "\$x = $x"; #prints: $x = 10
puts "5+5 = [expr {5+5}]"; #prints: 5+5 = 10
puts {5+5 = [expr {5+5}]}; #prints: 5+5 = [expr {5+5}]

Notice that brackets must be used around the expr command. In Tcl,  commands inside brackets get executed first and the output of the command is returned. The returned value can then be used. You can even do something confusing like

puts "4*(2+3) = [expr {4*[expr {2+3}]}]"; #prints: 4*(2+3) = 20

Below is a table of all the mathematical operators in order of precedence. This table was copied from https://wiki.tcl-lang.org/page/expr (which has a lot of great resources for learning Tcl).

-, +, ~, !Unary operators; specifically a negation operation, a non-negation operation, a bit-wise NOT operation (every bit in the input value gets replaced by its inverse) and a logical NOT operation (non-zero maps to zero, and zero maps to one).
**Exponential. From Tcl 8.5 on.
*, /, %Multiplication, division and integer remainder (see fmod() below).
+, –Addition and subtraction.
<<, >>Left and right shift. Equivalent to multiplying or dividing by a suitable power of two, and then reducing the result to the range representable in an integer on the host platform.
<, >, <=, >=Ordering relations: less than, greater than, less than or equal, greater than or equal. These operations work on strings as well as numbers, but where string comparison is intended, it is advisable to use the dedicated string comparison operators or string compare or string equal instead, as those are more predictable in the case of a string that looks like a number. For example, string equal considers “6” and “06” to be different strings, but the expr‘ operator == considers them to be equivalent numbers.
==, !=Equality and inequality. These operations work on strings as well as numbers, but see the description equality operators for notes about string comparison.
eq, ne
Since Tcl 8.4, these are string-comparison operators. “6” and “06”, as well as 1 and 1.0, will compare unequal.
in, niChecks for the occurrence of an item in a list. New in Tcl 8.5.
&Bit-wise AND. A bit is set in the result when the corresponding bit is set in both the arguments.
^Bit-wise exclusive OR. A bit is set in the result when the corresponding bit is set in precisely one of the arguments.
|Bit-wise OR. A bit is set in the result when the corresponding bit is set in either of the arguments.
&&Logical AND. The result is 1 when both of the arguments are true. and 0 otherwise. This operation is a short-circuiting operation, and will only evaluate its second argument when the first argument is non-zero. This includes the expansion of Tcl commands in square brackets. Where Tcl seems not to be behaving as describe here, see double substitution.
||Logical OR. The result is 0 when both of the arguments are false, and 1 otherwise. This operation is a short-circuiting operation, and will only evaluate its second argument when the first argument is zero. Where Tcl seems not to be behaving as describe here, see double substitution.
x?y:zIf-then-else, as in C. x, y, and z are expressions. The result is y if x is true, and z otherwise. This operation is a short-circuiting operation: If x is true, z will not be evaluated, and if x is false, y will not be evaluated. Where Tcl seems not to be behaving as describe here, see double substitution. if performs just as well as this construct. The generated bytecode is identical.

Exercise

In this exercise, we’re going to perform a simple magic trick.

  • Pick a number between 1-20 and store the number in a variable called favNum (favorite number) using the set command
  • Using the expr command, add 1 to the number and store it in a new variable called favNumMod (favorite number modified)with the set command
  • Next, double favNumMod and store the result in favNumModD (the D is for double)
  • Now add 4 to favNumModD and store the result in favNumFour
  • Divide favNumFour by 2 and then subtract favNum from the result. Store the answer in a new variable called result
  • Finally, print result using the puts command (e.g. puts “result = $result”)

I’m predicting the final answer you got was …. this is a tough one… 3! How did I do it? It’s magic. If you didn’t get 3, go back through the exercise and see what went wrong. You will always get 3 when doing this exercise regardless of the starting number. If you figured out the trick explain it in the comments!

Tip, if your having trouble you can print out the results of each step using puts. This is a common debugging step.

To help you out I’ll give an example of what the result of each step should look like using pseudocode (something written in code-like format but won’t actually run). I’ll use the number 4 as my starting point but you don’t have to.

  • favNum = 4
  • favNumM = 5
  • favNumD = 10
  • favNum4 = 14
  • result = 14/2-favNum

JOIN OUR NEWSLETTER
I agree to receive email updates when new content is posted and receive promotional emails when new courses are available
Enjoying the article? Receive free updates when new content is published and courses are available.
We hate spam. Your email address will not be sold or shared with anyone else.
Christopher Hogstrom
christopher.hogstrom@grittyengineer.com

Leave a Reply

Your email address will not be published. Required fields are marked *