ZetCode

Tcl中的流程控制

最后修改于 2023 年 10 月 18 日

在本Tcl教程中,我们讨论流程控制。我们定义了几个命令,使我们能够控制Tcl脚本的流程。

在Tcl语言中,有几个命令用于改变程序的流程。当程序运行时,它的命令从源文件的顶部到最底部依次执行。这种流程可以通过特定命令来改变。命令可以执行多次。有些命令是有条件的。它们只有在满足特定条件时才会执行。

if命令

if命令具有以下通用形式

if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN?

if命令用于检查一个表达式是否为真。如果为真,则执行一个或多个命令体。命令体由大括号括起来。

if命令计算一个表达式。表达式必须返回一个布尔值。在Tcl中,1、yes、true表示真,0、no、false表示假。

!/usr/bin/tclsh

if yes {
    puts "This message is always shown"
}

在上面的例子中,由{ }字符括起来的命令体总是被执行。

#!/usr/bin/tclsh

if true then {
    puts "This message is always shown"
}

then命令是可选的。如果我们认为它会使代码更清晰,就可以使用它。

我们可以使用else命令来创建一个简单的分支。如果if命令后面的方括号内的表达式计算结果为false,则自动执行else命令后面的命令。

#!/usr/bin/tclsh

set sex female

if {$sex == "male"} {

    puts "It is a boy"
} else {

    puts "It is a girl"
}

我们有一个sex变量。它包含字符串"female"。布尔表达式计算结果为false,我们在控制台中得到"It is a girl"。

$ ./girlboy.tcl 
It is a girl

我们可以使用elseif命令创建多个分支。elseif命令测试另一个条件,当且仅当先前的条件未满足时才会测试。请注意,我们可以在测试中使用多个elseif命令。

#!/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"
}

在上面的脚本中,我们有一个提示输入值的提示。我们测试该值是否为负数、正数或等于零。如果第一个表达式计算结果为false,则计算第二个表达式。如果先前的条件未满足,则执行else命令后面的命令体。

$ ./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

多次运行示例。

switch命令

switch命令将其字符串参数与每个模式参数按顺序匹配。一旦它找到一个与字符串匹配的模式,它就会通过递归地将其传递给Tcl解释器来评估后续的命令体参数,并返回该评估的结果。如果最后一个模式参数是default,那么它匹配任何内容。如果没有任何模式参数与字符串匹配,并且没有给出default,那么switch命令将返回一个空字符串。

#!/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" }
}

在我们的脚本中,我们提示输入一个域名。有几个选项。例如,如果该值等于us,则将字符串"United States"打印到控制台。如果该值与任何给定值都不匹配,则执行default命令体,并将unknown打印到控制台。

$ ./switch_cmd.tcl 
Select a top level domain name:sk
Slovakia

我们在控制台中输入了sk字符串,程序响应Slovakia。

while命令

while命令是一个流程控制命令,它允许根据给定的布尔条件重复执行代码。

while命令执行由大括号括起来的块内的命令。每次表达式计算结果为true时,都会执行这些命令。

#!/usr/bin/tclsh

# whileloop.tcl

set i 0
set sum 0

while { $i < 10 } {

    incr i
    incr sum $i
}

puts $sum

在代码示例中,我们计算某个数字范围内值的总和。

while循环有三个部分:初始化、测试和更新。每次执行命令称为一个周期。

set i 0

我们初始化 i 变量。它用作计数器。

while { $i < 10 } {
...
}

位于while命令后面的大括号内的表达式是第二阶段,即测试。命令体中的命令被执行,直到表达式的计算结果为false。

incr i

while循环的最后第三阶段是更新。计数器递增。请注意,不正确地处理while循环可能会导致无休止的循环。

for命令

当循环开始前循环的周期数已知时,我们可以使用for命令。在这个结构中,我们声明一个计数器变量,该变量在循环的每次重复期间都会自动增加或减少其值。

#!/usr/bin/tclsh

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

在此示例中,我们将数字 0..9 打印到控制台。

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

有三个阶段。首先,我们将计数器i初始化为零。此阶段仅执行一次。接下来是条件。如果满足条件,则执行for块内的命令。然后是第三阶段;计数器递增。现在我们重复阶段2和3,直到条件不满足并且for循环退出。在我们的例子中,当计数器i等于10时,for循环停止执行。

$ ./forloop.tcl 
0
1
2
3
4
5
6
7
8
9

在这里,我们看到了forloop.tcl脚本的输出。

foreach命令

foreach命令简化了对数据集合的遍历。它没有显式的计数器。它逐个元素地遍历列表,并将当前值复制到构造中定义的变量中。

#!/usr/bin/tclsh

set planets { Mercury Venus Earth Mars Jupiter Saturn
    Uranus Neptune }

foreach planet $planets {
    puts $planet
}

在本例中,我们使用foreach命令遍历行星列表。

foreach planet $planets {
    puts $planet
}

foreach命令的用法很简单。planets是我们遍历的列表。planet是临时变量,它具有列表中的当前值。foreach命令遍历所有行星并将它们打印到控制台。

$ ./planets.tcl 
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune

运行上面的Tcl脚本将给出此输出。

#!/usr/bin/tclsh

set actresses { Rachel Weiss Scarlett Johansson Jessica Alba \
    Marion Cotillard Jennifer Connelly}

foreach {first second} $actresses {
    puts "$first $second"
}

在这个脚本中,我们遍历列表中的值对。

foreach {first second} $actresses {
    puts "$first $second"
}

我们在每次迭代中从列表中选取两个值。

$ ./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"
}

我们可以并行遍历两个列表。

$ ./parallel.tcl 
one car
two coins
three rocks

break和continue命令

break命令可用于终止由whileforswitch命令定义的块。

#!/usr/bin/tclsh

while true {

    set r [expr 1 + round(rand()*30)]
    puts -nonewline "$r "

    if {$r == 22} { break }
}

puts ""

我们定义了一个无休止的while循环。我们使用break命令来退出此循环。我们选择一个介于1到30之间的随机值并打印它。如果该值等于22,我们结束无休止的while循环。

set r [expr 1 + round(rand()*30)]

这里我们计算1..30之间的随机数。rand是一个内置的Tcl过程。它返回一个介于0到0.99999之间的随机数。rand()*30返回一个介于0到29.99999之间的随机数。round过程对最终数字进行四舍五入。

$ ./breakcommand.tcl 
28 20 8 8 12 22 

我们可能会得到类似这样的结果。

continue命令用于跳过循环的一部分并继续循环的下一次迭代。它可以与forwhile命令结合使用。

在以下示例中,我们打印一个无法被 2 整除而没有余数的数字列表。

#!/usr/bin/tclsh

set num 0

while { $num < 100 } {

    incr num

    if {$num % 2 == 0} { continue }
 
    puts "$num "
}

puts ""

我们使用 while 循环迭代数字 1..99。

if {$num % 2 == 0} { continue }

如果表达式num % 2返回0,则可以被2整除。执行continue命令,跳过循环的其余部分。在我们的例子中,跳过了循环的最后一个命令,并且未将该数字打印到控制台。开始了下一次迭代。

在本Tcl教程中,我们讨论了流程控制结构。