这个TCL代码绝对神秘

时间:2014-04-18 04:33:37

标签: if-statement while-loop tcl

我刚刚开始在TCL中编码,并且有这个while循环代码:

set x 0
while "$x < 5" {
    set x [expr {$x + 1}]
    if {$x > 7} break
    if "$x > 3" continue
    puts "x is $x"
}

puts "exited second loop with X equal to $x"

哪个输出:

x is 1
x is 2
x is 3
exited second loop with X equal to 8
Press any key to continue . . .

这绝对令我困惑。我很好奇,将"$x > 3"评估为[expr {"$x > 3"}]或好像(这是一个字符串,因为它本身只是一个字符串,它是真的。)

它是如何工作的?给我机械师。巨大的偏头痛试图解决这个问题。

3 个答案:

答案 0 :(得分:5)

Tcl有一个非常简单的评估模型。它总是在看到它们的时候评估它们;如果一个单词在大括号中,则它不会被取消,但除此之外它有$[…](以及\,但您不会使用这些替换; "…"只是将一堆字符组合成一个单词(即,它会覆盖通常的空格分隔规则)。一旦它组装了单词,它就会将它们发送到命令实现(从第一个单词查找)以便执行。那就是它。

您建议始终支持您的表达式,因为这可以避免混淆(并使Tcl能够编译代码)。


适用于您的案例

set x 0

没有替换。三个词。 set0写入变量x

while "$x < 5" {
    set x [expr {$x + 1}]
    if {$x > 7} break
    if "$x > 3" continue
    puts "x is $x"
}

仅在第二个单词中替换,变为0 < 5。其他的话是无替代的。虽然第二个单词(解释为表达式)的计算结果为真值,但while会将第三个单词计算为脚本,直到它产生一个合适的异常(错误,中断)。专业提示:表达总是正确的!

循环,第一次迭代

查看循环体内部,我们看到第一个命令是:

set x [expr {$x + 1}]

我们已经使用expr(评估表达式$x + 1,生成1)进行了命令替换,这是整个第三个单词。最终结果是set将值1置于变量x中。

if {$x > 7} break

嗯,if $x > 7评估为真(它没有),评估脚本break。不评估脚本。

if "$x > 3" continue

if 1 > 3(这是双引号,因此请立即替换)评估脚本continue。不评估脚本。

puts "x is $x"

x is 1传递给puts,然后打印出来。

循环,第二次/第三次迭代

set x [expr {$x + 1}]
if {$x > 7} break
if "$x > 3" continue
puts "x is $x"

与上述相同,但2 / 3代替1除外。

循环,第四次迭代

这一次,当我们到达

if "$x > 3" continue

我们最终得到表达式4 > 3,这是真的,所以我们评估“body”脚本continue。这是一个生成继续异常的简单命令。 while捕获并返回并再次测试循环条件(并开始第五次迭代)。最终效果是跳过循环体末端的puts

迭代5,6,7

这些与迭代4非常相似。

请注意,"…"仅在到达时进行评估。这意味着对于循环内部的循环,它们每次循环都会被评估/替换。但是,{<1}}的循环条件是每次都被重新替换,因为在调用命令之前发生了替换。 (它与表达式有点混淆,因为它们支持while$以及[…]的类似Tcl的语法,但它们&#39;正式地使用单独的解析器进行单独的语言。)

迭代8

第一行的"…" / set会将expr设置为x。当它到达下一行时:

8

表达式将评估为true(因为变量if {$x > 7} break 的内容大于x)并且将评估7。这会产生一个中断异常,导致break完成。

离开循环

现在,我们再次出局并处理最后一个命令:

while

你需要对此进行演练吗?


确切地了解

的内容

您可以使用this question中的命令执行跟踪代码来查看确切的内容。只需将您的代码放入一个程序中,应用跟踪器,然后将其激活,以确切了解发生了什么,每一个细节。 (好吧,只有替换的结果,但你可以从中找出答案。)

在这里,我将告诉你:

puts "exited second loop with X equal to $x"

产生此输出:

proc YourCode {} {
    set x 0
    while "$x < 5" {
        set x [expr {$x + 1}]
        if {$x > 7} break
        if "$x > 3" continue
        puts "x is $x"
    }

    puts "exited second loop with X equal to $x"
}
trace add execution YourCode {enterstep leavestep} showCalls
proc showCalls {cmd args} {
    switch [lindex $args end] {
        enterstep {
            incr counter
            puts >>>$cmd
        }
        leavestep {
            lassign $args code result
            puts "<<<$cmd <<<$code,$result<<<"
        }
    }
}

YourCode

(在某些值周围插入大括号;它们是我们打印参数的方式的假象。对于异常代码,0表示正常,3表示中断,4表示继续。另一个标准一个是1个用于错误,2个用于从范围返回。)

答案 1 :(得分:0)

该文件说:

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

if命令将expr1计算为表达式(与expr计算其参数的方式相同)。 ...

这意味着$x > 3被评估为使用expr

我注意到您使用双引号而不是whileif条件的大括号。请使用大括号,如:

while {$x < 5} { ... }

同样地:

if {$x > 3} { ... }

答案 2 :(得分:0)

如果表达式/ test / condition未包含在大括号内,则在while命令开始执行之前将进行变量替换,这意味着循环体所做的变量更改将不会在表达式中被考虑。