TCLWISE
An introduction to the Tcl programming language

Sponsored Project: The Jim interpreter
A small footprint implementation of Tcl

Send a comment to the author


8. CONTROL CONSTRUCTS


In the last chapters we focused enough into the goal of learning how Tcl works, that some important core command escaped our attention. Now the reader should be familiar enough with the structure of Tcl programs to learn some new interesting command without too efforts. All the commands covered in this chapter are about controlling the flow of the program. We already saw if, while and foreach, but there are other interesting control constructs to discover.

8.1 The switch command


The switch command is very useful when we want to test the value of a string against a number of patterns, and execute a different code based on the matching pattern. You can see switch as a special case of if, for instance the code:

proc number2word n { if {$n == 0} { return "zero" } elseif {$n == 1} { return "one" } elseif {$n == 2} { return "two" } else { return "hello!" } }

can be translated in this using switch:

proc number2word n { switch $n { 0 {return "zero"} 1 {return "one"} 2 {return "two"} default {return "hello!"} } }

As you can see the second version is more readable. In the simple form switch takes two arguments:


switch string {pattern body pattern body ?pattern body? ...}


The first argument is the string we want to test against the patterns, the second argument is a list with pairs of pattern to match against, and the body of the script to execute if there is a match for this pattern. If the last pattern is the string "default", the relative body will be executed if no one of the previous patterns matched.

That's the main idea of switch, but actually the command is a bit more complex, because it supports another form, and a number of options that change the way the string is matched against the patterns. This is the complete signature of the command:


switch ?options? string {pattern body ?pattern body ...?}


or, in alternative:


switch ?options? string pattern body ?pattern body ...?


This second form just means that instead to pass the pattern body pairs into a list it is also possible to pass they directly as switch arguments. You may like this form when writing very short code like:

switch $a 1 {return one} 2 {return two} 3 {return three}

But in general the first form is better from the point of view of the indentation. Another thing to note is that switch can take options to modify his behaviour. The supported options are:

-exact Use exact matching when comparing the string with patterns. -glob Use glob style matching, like the [string match] command. -regexp Use regular expressions for matching. -- End of options, all the rest is interpreted as an argument even if it starts with a - character.

The default matching type is -glob, this is not very comfortable because most of the time you may want to match strings with exact matching (i.e. using -exact). Another problem with switch options is that if the string argument starts with a - character, it is interpreted as an option, unless there is a -- argument just after the command name. So, most of the time you want to use something like this:

switch -exact -- $string { 1 {return one} 2 {return two} 3 {return three} default {return Hello} }

This is the safe way to use switch. Of course if you like to use glob style or regexp matching, you can substitute -exact with what you need.

Starting with Tcl 8.5, switch supports two new options, they are -matchvar and -indexvar. Both the options are about the regexp matching style (-regexp option) and are not vital in this stage of your Tcl experience, but be sure to check the man page if you need to use regexp matching with switch and want to store in variables the match or submatches of the matching pattern.

It's worth to note that like the if command, switch returns the value of the last command executed in the matching branch, so for instance it's possible to write something like:

proc identity x {return $x} ; # The identity function, returns its argument. set word [switch $number 1 {identity one} 2 {identity two}]

The identity function is just used to return the value we are interested to assign to the word variable if $x matches one of the patterns.

8.2 The for command


The Tcl for command is modelled after the C language one, like in many other languages. for is used to loop, i.e. to repeat the same fragment of code multiple time, in this respect it is very similar to the while command, but a bit more complex. The more common use of for is to run a variable over an arithmetic progression.

The following is an example of for loop, used to count from 0 to 9:

for {set i 0} {$i < 10} {incr i} { puts $i }

If you try to execute this code, the output is

0 1 2 ...

up to 9. If you look at the code, it's easy to notice that for takes four Tcl scripts as arguments, this is the signature for the for command:


for start test next body


The for loop starts evaluating the start script, that in our example is the Tcl code "set i 0". This will set the variable i to the value of zero. The start script is evaluated only one time, as first step of a for loop. Now the loop can start, following this rules:

The test argument is evaluated using expr, if the result is true the body script is executed, then the next script is executed. An interation is now completed, and the loop restart testing against if test is true. The first time the test is false, the for loop terminates.

The command always returns an empty string. Note that it's possible to put every kind of Tcl script in the start, next, and body arguments, and every kind of valid expr expression in the test argument. For example this is an infinite loop:

for {} 1 {} { puts "I'll print this forever" }

Similarly it's possible to use more than a variable:

for {set x 0; set y 0} {$x+$y != 50} {incr x 2; incr y 3} {puts .} puts "$x+$y = 50"

The above code will increment x by 2 and y by 3, and will stop when the sum of the two will be 50 (after 10 iterations). In the example the body argument is empty because the work needed to continue the computation is in the next script.

8.3 break and continue


Tcl core commands implementing looping constructs like foreach, while, and for, support a way to exit prematurely from the loop, or to prematurely reiterate it before all the commands of the body of the loop are executed. In order to control this features, there are two Tcl commands called break and continue. They are modelled after the C programming language break and continue keywords.

The break command is the one used to prematurely exit from the loop. It may appear at any place in the body of the loop. If there are nested loops and the break command is executed, only the innermost loop will terminate. In Tcl break does not take any argument, and there is no way to specify how many levels to exit. This is an example of code using break:

set a 0 while 1 { puts $a incr a if {$a == 10} break }

The output of the program is a sequence of numbers from 0 to 9. Note that even if the while loop apperas to be an infinite loop (because the condition is true being just 1, and will never change), break allows to exit from the loop if a given condition is encountered (that the variable a contains the value of 10).

Calling the break command outside a loop will produce an error, because there is no loop to escape.

You may wonder how it's possible to exit multiple nexted loops if a break command will only terminate the innermost one. This example contains two nested loops:

while 1 { while 1 { break } }

In order to exit two levels, we can use the following trick:

set dobreak 0 while 1 { while 1 { set dobreak 1; break } if {$dobreak} break }

This time in order to exit both the loops we initialize a dobreak variable to zero, and test it every time we exit from the inner loop, calling break again in the outside loop if it is set to 1. In the inner loop we can use just break when we want to escape only one loop, or set dobreak 1; break when we want to escape both.

You will hardly need to use this kind of tricks because the need to break multiple loops is not common, but it may be useful to know how to do it if really needed.

Note that break works exactly in the same way with for, foreach and other looping commands.

If you are a C programmer, you may remember that in C it may happen to have a while or for loop with a nested switch inside. Because in C the switch requires break in order to terminate a case you can't escape the loop from the switch using break. Instead something like a flag variable similar to the dobreak above, or the use of goto, is required. In Tcl this problems does not exist because Tcl's switch does not use the break in any way, so a break inside the switch will produce the desidered effect of terminating the innermost loop.

The external loop will continue to run forever, because the break of the nexted while loop will only affect the.

The continue command is similar to break, but instead to terminate the innermost loop in execution, it will just reiterate it. It's important to specify what to reiterate means in this context: when continue is encountered what happens is exactly what happens if there are no longer commands to execute in the body of the loop. In the case of a while loop the test condition will be evaluated again, and the loop body will be executed if it is still true. In the case of foreach the next element(s) of the list(s) will be assigned to the foreach variable(s), and the body will be executed again, and so on.

The following is an example of continue:

foreach x {0 1 2 3 4 5 6 7 8 9} { if {$x < 3} continue puts $x }

The above Tcl programm will output the numbers from 3 to 9, because at every iteration, if $x < 3 is true, the loop will reiterate without to execute the puts $x command.

8.4 The lack of goto


In Tcl there isn't a goto command, there are many reasons to avoid goto designing an high level programming language, actually the author of this book have good uses for goto in C, in order to deal with exceptions or to escape nested loops, but when programming in Tcl it's very hard to miss a goto command. Tcl is at higher level: there is exception handling, switch inside loops can use break to terminate the loop, and in general working at higher level the lack of a goto command is not an issue. Most of the times when you require goto in C, you may want to implement a state machine in Tcl.

Still, the Tcl goal is the flexibility, in the next chapter you will learn how it is possible to write Tcl commands implementing new control structures in Tcl itself, including things similar to goto.

For know it can be interesting to remember that it's possible to use foreach and break to implementing something that may resemble an unconditional jump, like in the following code:

foreach _ _ { ... some code ... if {$condition} break ; # Will exit this "block" ... some other code ... }

the foreach will iterate just one time using the variable _ in a list composed of one element (the "_" string). When the foreach body will call break the body will terminate.

Other Tcl/Tk books
Index
2.1 Anatomy of a command
2.2 Grouping
2.3 Program structure
2.4 Substitution of commands
2.5 Substitution of variables
2.6 More on interpolation
2.7 Comments
2.8 That's it
3.1 User defined procedures
3.2 The if command
4.1 Tcl list
4.2 The foreach command
4.3 The lrange command
4.4 The lappend command
4.5 The lset command
4.6 The lsort command
4.7 List values against variable names
5.1 The append command
5.2 The string command
5.3 string range
5.4 string index
5.5 string equal
5.6 string compare
5.7 string match
5.8 string map
5.9 string is
5.10 More string subcommands
5.11 Advanced string matching
6.1 Converting strings to lists
6.2 From strings to list of chars
6.3 Converting lists to strings
6.4 Manipulating strings as lists
7.1 Local variables
7.2 Top level
7.3 Global variables
7.4 Procedures arguments and pass by value
7.5 Procedures with a variable number of arguments
7.6 Procedures with default arguments
7.7 Recursion
7.8 Recursion limit
8.1 The switch command
8.2 The for command
8.3 break and continue
8.4 The lack of goto
9.1 Programs executing programs: the eval command
9.2 Breaking the rules with uplevel
9.3 Passing variable names to procedures
9.4 Mapping scripts to lists
9.5 The rename command
9.6 Expanding lists into arguments in Tcl 8.5
Additional 20 chapters in the printed version.

Related man pages


Links
Author Home
Tclers Wiki


Copyright © 2004 Salvatore Sanfilippo. All rights reserved.
This online book is for personal use only.
It cannot be copied to other web sites or further distributed in any form.