当前位置:文档之家› TCL脚本语言-8-控制结构

TCL脚本语言-8-控制结构

控制结构TCL中使用命令来实现程序控制结构,我们还可以自己编写扩展命令来扩充控制结构。

常见的控制结构包括如下几种:循环控制、条件判断、异常处理和执行脚本。

这些结构控制命令都比较简单,和C/C++中的程序控制比较类似。

但是在介绍控制结构之前,先有必要介绍一下Tcl中的布尔类型。

Boolean类型再次强调,TCL中一切都是字符串,没有类型。

这里所说的Boolean类型,指的是在条件判断的时候,什么东西被当作True,什么被当作False。

请记住如下规则:1.字符串:Yes,Y,True,T,On。

不管大小写,当作条件来判断时,会被认为是True;2.字符串:No,N,False,F,Off。

不管大小写,当作条件来判断时,会被认为是False;3.数字0(整数或者浮点)会被当作False,任何非0的整数或者浮点数都会被当作True;例如:% while 0.00e23 "puts ccc;break;" ;#False% while 0. "puts ccc;break;" ;#False% while 0.1 "puts ccc;break;" ;#Trueccc% while T "puts ccc;break;" ;#Trueccc% while 1023 "puts ccc;break;" ;#Trueccc% while T "puts ccc;break;" ;#Trueccc条件判断TCL中的条件判断主要是两个命令:if和switch。

if…elseif命令的格式如下:if expr1 ?then? body1elseif expr2 ?then? body2elseif ... ?else? ?bodyN?可以看见,if命令的参数then是可以省略的。

这给喜欢Pascal风格和C风格的不同的编程人员,都带来了怀旧的机会。

Pascal中then是必须的。

C中是没有then的。

if命令中的条件表达式expr可以写成多行,只要是一个字符串都可以。

如果写成多行,那么最好加上then,这样看起来就比较方便。

这个命令的具体执行我就不详细解释了。

要注意的是最后的else,如果没有else,那么就不能有bodyN,如果有了else,就必须有bodyN。

请看例子:if {$vbl == 1} {puts "vbl is one"} elseif {$vbl == 2} {puts "vbl is two"} else {puts "vbl is not one or two"}switch相比if…else,switch命令则要复杂很多。

它有两种语法形式:1.switch ?options? string pattern body ?pattern body ...?2.switch ?options? string {pattern body ?pattern body ...?}两种形式功能上完全等价,我们一般建议采用第二种,它和C语言的语法比较接近:所有的候选值和对应的代码全部都放在一个花括号中。

但是如果需要对pattern和body等进行替换,那么第一个形式就比较方便。

switch的选项参数可以为:1.-exact:这是默认情况,表示参数string和模式pattern的精确匹配;2.-glob:表示采用glob模式进行匹配。

匹配方法参考string match命令;3.-regexp:表示采用正则表达式进行匹配,string是字符串,而pattern是正则表达式;4.--:表示选项参数到此为止,后面的是参数string。

这是为了防止参数string的第一个字符就是“-”而引起非法选项的错误。

该命令的执行过程:根据选项指定的匹配模式,逐个将string和pattern进行匹配,如果匹配成功,那么就执行pattern后面对应的代码,并且返回该代码的结果。

如果最后的一个模式是default,安么它匹配任意字符串。

如果string没有一个匹配成功并且没有default,那么switch直接返回空。

如果某一个pattern对应的body为“-”,那么表示这个pattern和下一个pattern共享一个body。

这和C语言中的case 1:case2:{…}比较类似。

switch中的-regexp请看下面的几个例子:set r "abbbcc"switch -regexp $r {ab{2}c { puts "1" }ab{3}c { puts "2" }ab{3}c{2} { puts "3" }}上面采用正则表达式进行匹配,显然,第三个表达式才是和$r完全匹配的。

但是上面的代码执行输出是“2”。

这给我们一个重要的启示:switch中的-regexp匹配选项,实质上就是把pattern拿出来在string中进行正则表达式查找,也就是执行regexp命令,如果regexp返回1(也就是在string中找到了pattern),那么就算匹配成功,就开始执行后面的body。

而根本不管pattern是不是恰好匹配了整个string!上面这一点务必牢记。

对-glob选项,则没有这样的问题。

选项pattern和string只有完全匹配的时候才算匹配成功。

也就是说执行命令string match $pattern $string返回1,才算匹配成功。

注释的位置如果使用switch的第二种语法形式,那么就要注意switch中注释的正确位置。

注释只有放在各个body中才是正确的注释,如果放在其它地方,会出现语法错误。

例如:set r "abbbcc"switch -glob $r {a {#匹配失败,不可能执行到这里puts "1"#正确的注释}a*cc { puts "2" }#放到这里,是错误的注释ab??cc {puts "3"}}我使用的是8.4.9版本,出现的错误信息还非常的人性化:extra switch pattern with no body, this may be due to a comment incorrectly placedoutside of a switch body - see the "switch" documentationwhile executing会提醒你可能是注释放错了地方,以前的版本就没有这么好了。

错误信息会让你不知所云。

循环控制TCL中使用while、for、foreach命令来实现循环控制结构。

除了这三个命令之外,tcl 程序包control还提供了do循环。

别忘了,前面的章节中,我们还自己实现了一个简单的do…while循环。

while循环while循环命令的语法格式如下:while test body其中test被当作一个表达式来求值(和expr命令一样),结果必须是一个合法的布尔值。

当test的值为True的时候,循环体body会被执行;body执行完之后,test会被再次求值,直到test为False为止。

while命令总是返回一个空的字符串。

如果body中包含continue,那么当前这一次循环迭代会被中止,然后直接计算test,根据结果判断是否继续下一次循环;如果body中包含break,那么整个while循环立刻全部中止。

前面的章节中我们介绍TCL置换原理的时候强调了,while命令的test条件必须放在华括号中,这样每次循环结束后才对test进行真正的求值。

如果不放在花括号中,就可能会出现死循环。

有些初学者会写出如下的代码:set x 100while {[expr $x>=50 && $x<=100 ]} {……}这样的代码错了吗?没有错误,但是不好。

while循环中的判断条件,我们没有必要写在expr命令中来求值,因为while命令会自动的进行expr计算。

所以如下的代码更好:while {$x>=50 && $x<=100} {……}for循环Tcl中的for循环和C中的类似,其语法如下:for start test next body其中start,next和body都是可以执行的TCL脚本;test和while中的test类似被当作一个表达式来求值,其结果作为循环是否继续的判断标准。

for循环的执行过程如下:1.首先执行start;2.计算表达式test;3.如果test的值是True,那么就执行body,然后再执行next;然后跳到第2步;4.如果test的值是False,那么就停止循环;如果在body或者next中执行了break命令,那么for循环马上中止;如果在body中执行了continue命令,那么body中剩下的命令全部跳过而继续执行next,然后再计算next,根据结果判断是否继续循环;这两个命令在for循环中的表现和C语言中的breakl以及continue类似。

一般而言,test都必须放在花括号中,这一点和while循环类似。

下面是for循环的例子:for {set x 1} {$x<=1024} {set x [expr {$x * 2}]} {puts "x is $x"}上面的代码打印出从1开始到1024中所有的2的n次方。

foreach 循环foreach提供了一种简单的在一个或者多个列表上进行迭代的方法。

它有两种形式:foreach varname list bodyforeach varlist list1 ?varlist2 list2…? body第一种语法中,只利用一个变量对列表list进行迭代,每一次循环,都顺序取一个列表元素的值赋给varname,然后执行body。

第二种语法中,可以同时对多个列表进行迭代。

在迭代的时候,可以一次取出一个列表多个元素。

实际上,第一中形式只是第二种语法的一个特例而已。

看看下面的几个例子:set x {}foreach {i j} {a b c d e f} {lappend x $j $i}#执行后,x的值为"b a d c f e";总共完成了3次迭代。

#下面的迭代中,同时针对两个列表进行迭代:set x {}foreach i {a b c} j {d e f g} {lappend x $i $j}#执行后,x的值为"a d b e c f {} g";总共完成了4的迭代。

相关主题