Flow control in Tcl
last modified October 18, 2023
In this part of the Tcl tutorial, we talk about the flow control. We define several commands that enable us to control the flow of a Tcl script.
In Tcl language there are several commands that are used to alter the flow of a program. When a program is run, its commands are executed from the top of the source file to the bottom. One by one. This flow can be altered by specific commands. Commands can be executed multiple times. Some commands are conditional. They are executed only if a specific condition is met.
The if command
The if command has the following general form:
if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN?
The if command is used to check if an expression
is true. If it is true, a body of command(s) is then executed.
The body is enclosed by curly brackets.
The if command evaluates an expression. The expression must return
a boolean value. In Tcl, 1, yes, true mean true and 0, no, false mean
false.
!/usr/bin/tclsh
if yes {
puts "This message is always shown"
}
In the above example, the body enclosed by { } characters is always
executed.
#!/usr/bin/tclsh
if true then {
puts "This message is always shown"
}
The then command is optional. We can use it if we think, it
will make the code more clear.
We can use the else command to create a simple branch.
If the expression inside the square brackets following the if command
evaluates to false, the command following the else
command is automatically executed.
#!/usr/bin/tclsh
set sex female
if {$sex == "male"} {
puts "It is a boy"
} else {
puts "It is a girl"
}
We have a sex variable. It has "female" string. The boolean expression evaluates to false and we get "It is a girl" in the console.
$ ./girlboy.tcl It is a girl
We can create multiple branches using the elseif command.
The elseif command tests for another condition, if and only if
the previous condition was not met. Note that we can use multiple
elseif commands in our tests.
#!/usr/bin/tclsh
# nums.tcl
puts -nonewline "Enter a number: "
flush stdout
set a [gets stdin]
if {$a < 0} {
puts "the number is negative"
} elseif { $a == 0 } {
puts "the numer is zero"
} else {
puts "the number is positive"
}
In the above script we have a prompt to enter a value. We test the value
if it is a negative number or positive or if it equals to zero. If the first
expression evaluates to false, the second expression is evaluated.
If the previous conditions were not met, then the body following the
else command would be executed.
$ ./nums.tcl Enter a number: 2 the number is positive $ ./nums.tcl Enter a number: 0 the numer is zero $ ./nums.tcl Enter a number: -3 the number is negative
Running the example multiple times.
The switch command
The switch command matches its string argument against each of the pattern
arguments in order. As soon as it finds a pattern that matches the string it
evaluates the following body argument by passing it recursively to the Tcl
interpreter and returns the result of that evaluation. If the last pattern
argument is default then it matches anything. If no pattern argument matches
string and no default is given, then the switch command returns an empty string.
#!/usr/bin/tclsh
# switch_cmd.tcl
puts -nonewline "Select a top level domain name:"
flush stdout
gets stdin domain
switch $domain {
us { puts "United States" }
de { puts Germany }
sk { puts Slovakia }
hu { puts Hungary }
default { puts "unknown" }
}
In our script, we prompt for a domain name. There are several options. If the value equals for example to us the "United States" string is printed to the console. If the value does not match to any given value, the default body is executed and unknown is printed to the console.
$ ./switch_cmd.tcl Select a top level domain name:sk Slovakia
We have entered sk string to the console and the program responded with Slovakia.
The while command
The while command is a control flow command
that allows code to be executed repeatedly based on a given boolean condition.
The while command executes the commands inside the
block enclosed by curly brackets. The commands are executed each time
the expression is evaluated to true.
#!/usr/bin/tclsh
# whileloop.tcl
set i 0
set sum 0
while { $i < 10 } {
incr i
incr sum $i
}
puts $sum
In the code example, we calculate the sum of values from a range of numbers.
The while loop has three parts: initialization, testing, and
updating. Each execution of the command is called a cycle.
set i 0
We initiate the i variable. It is used as a counter.
while { $i < 10 } {
...
}
The expression inside the curly brackets following the while
command is the second phase, the testing. The commands in the body are
executed, until the expression is evaluated to false.
incr i
The last, third phase of the while loop is the updating. The counter
is incremented. Note that improper handling of the while loops may lead
to endless cycles.
The for command
When the number of cycles is know before the loop is initiated,
we can use the for command. In this construct
we declare a counter variable, which is automatically increased
or decreased in value during each repetition of the loop.
#!/usr/bin/tclsh
for {set i 0} {$i < 10} {incr i} {
puts $i
}
In this example, we print numbers 0..9 to the console.
for {set i 0} {$i < 10} {incr i} {
puts $i
}
There are three phases. First, we initiate the counter i to zero.
This phase is done only once. Next comes the condition. If the
condition is met, the command inside the for block is executed.
Then comes the third phase; the counter is increased. Now we repeat
phases 2 and 3 until the condition is not met and the for loop
is left. In our case, when the counter i is equal to 10,
the for loop stops executing.
$ ./forloop.tcl 0 1 2 3 4 5 6 7 8 9
Here we see the output of the forloop.tcl script.
The foreach command
The foreach command simplifies traversing over
collections of data. It has no explicit counter. It goes through a list
element by element and the current value is copied to a variable
defined in the construct.
#!/usr/bin/tclsh
set planets { Mercury Venus Earth Mars Jupiter Saturn
Uranus Neptune }
foreach planet $planets {
puts $planet
}
In this example, we use the foreach command to go
through a list of planets.
foreach planet $planets {
puts $planet
}
The usage of the foreach command is straightforward.
The planets is the list that we iterate through.
The planet is the temporary variable that has the current
value from the list. The foreach command goes through all the planets
and prints them to the console.
$ ./planets.tcl Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune
Running the above Tcl script gives this output.
#!/usr/bin/tclsh
set actresses { Rachel Weiss Scarlett Johansson Jessica Alba \
Marion Cotillard Jennifer Connelly}
foreach {first second} $actresses {
puts "$first $second"
}
In this script, we iterate througn pairs of values of a list.
foreach {first second} $actresses {
puts "$first $second"
}
We pick two values from the list at each iteration.
$ ./actresses.tcl Rachel Weiss Scarlett Johansson Jessica Alba Marion Cotillard Jennifer Connelly
#!/usr/bin/tclsh
foreach i { one two three } item {car coins rocks} {
puts "$i $item"
}
We can iterate over two lists in parallel.
$ ./parallel.tcl one car two coins three rocks
The break and continue commands
The break command can be used to terminate
a block defined by while, for,
or switch commands.
#!/usr/bin/tclsh
while true {
set r [expr 1 + round(rand()*30)]
puts -nonewline "$r "
if {$r == 22} { break }
}
puts ""
We define an endless while loop. We use the break
command to get out of this loop. We choose a random value from 1 to 30 and print it.
If the value equals to 22, we finish the endless while loop.
set r [expr 1 + round(rand()*30)]
Here we calculate a random number between 1..30. The rand is a built-in
Tcl procedure. It returns a random number from 0 to 0.99999.
The rand()*30 returns a random number between 0 to 29.99999.
The round procedure rounds the final number.
$ ./breakcommand.tcl 28 20 8 8 12 22
We might get something like this.
The continue command is used to skip a
part of the loop and continue with the next iteration of the loop.
It can be used in combination with for and
while commands.
In the following example, we print a list of numbers that cannot be divided by 2 without a remainder.
#!/usr/bin/tclsh
set num 0
while { $num < 100 } {
incr num
if {$num % 2 == 0} { continue }
puts "$num "
}
puts ""
We iterate through numbers 1..99 with the while loop.
if {$num % 2 == 0} { continue }
If the expression num % 2 returns 0, the number in question can be
divided by 2. The continue command is executed and the rest of the
cycle is skipped. In our case, the last command of the loop is skipped and the
number is not printed to the console. The next iteration is started.
In this part of the Tcl tutorial, we were talking about control flow structures.