这个TCL代码绝对神秘

0 if-statement tcl while-loop

我刚刚开始在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"
Run Code Online (Sandbox Code Playgroud)

哪个输出:

x is 1
x is 2
x is 3
exited second loop with X equal to 8
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)

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

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

Don*_*ows 5

Tcl有一个非常简单的评估模型.它总是在看到它们的时候评估它们; 如果一个单词在大括号中,则它是未被取消的,但是否则它已经$[…](并且\你没有使用那些)替换应用; 的"…"只是组一堆charaters连成一个字(即,它覆盖通常的空间分隔规则).一旦汇总了这些单词,它就会将它们发送到命令实现(从第一个单词中查找)以便执行.而已.

建议您始终使用表达式来避免混淆(并使Tcl能够编译代码).


适用于您的情况

set x 0
Run Code Online (Sandbox Code Playgroud)

没有换人.三个词.set写入0变量x.

while "$x < 5" {
    set x [expr {$x + 1}]
    if {$x > 7} break
    if "$x > 3" continue
    puts "x is $x"
}
Run Code Online (Sandbox Code Playgroud)

仅在第二个单词中的替换,变为0 < 5.其他的话是无替代的.当第二个单词(解释为表达式)求值为真值时,while将第三个单词计算为脚本,直到它产生合适的异常(错误,中断).专业提示:表达总是如此!

循环,第一次迭代

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

set x [expr {$x + 1}]
Run Code Online (Sandbox Code Playgroud)

我们有一个命令替换expr(用于评估表达式$x + 1,生成1),这就是整个第三个单词.最终结果是set将值1置于变量中x.

if {$x > 7} break
Run Code Online (Sandbox Code Playgroud)

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

if "$x > 3" continue
Run Code Online (Sandbox Code Playgroud)

if 1 > 3(有双引号,所以立即替换)评估脚本continue.不评估脚本.

puts "x is $x"
Run Code Online (Sandbox Code Playgroud)

通行证x is 1puts,打印它.

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

set x [expr {$x + 1}]
if {$x > 7} break
if "$x > 3" continue
puts "x is $x"
Run Code Online (Sandbox Code Playgroud)

与上面相同,除了2/ 3而不是1.

循环,第四次迭代

这一次,当我们到达

if "$x > 3" continue
Run Code Online (Sandbox Code Playgroud)

我们最终得到了表达式4 > 3,这是真的,所以我们评估"body"脚本,continue.这是一个生成继续异常的简单命令.该while渔获这一点,可以追溯到并测试一次循环条件(并启动第五次迭代).净效果是跳过puts循环体的末尾.

迭代5,6,7

这些与迭代4非常相似.

请注意,"…"仅在到达时才进行评估.这意味着对于循环内部的循环,它们每次循环时都会被评估/替换.但是,由于调用命令之前发生了替换,因此每次循环while不会重新替换循环条件.(这与表达式有点混淆,因为它们支持类似Tcl的语法和和,但它们正式是一个单独的语言,带有单独的解析器.)$[…]"…"

迭代8

顶行的set/ expr将设置x8.当它击中下一行时:

if {$x > 7} break
Run Code Online (Sandbox Code Playgroud)

表达式将计算为true(因为变量的内容x大于7)并且break将对其进行求值.这会产生一个中断异常,导致while完成.

跳出循环

现在,我们又在外面处理最后一个命令:

puts "exited second loop with X equal to $x"
Run Code Online (Sandbox Code Playgroud)

你需要一个演练吗?


看看究竟发生了什么

您可以使用此问题中的命令执行跟踪代码的调整来确切了解正在发生的事情.只需将您的代码放入一个过程中,应用跟踪器,然后将其关闭以查看确切的内容,每个细节.(好吧,只有替换的结果,但你可以从中找出答案.)

在这里,我将告诉你:

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
Run Code Online (Sandbox Code Playgroud)

哪个产生这个输出:

>>>set x 0
<<<set x 0 <<<0,0<<<
>>>while {0 < 5} {
            set x [expr {$x + 1}]
            if {$x > 7} break
            if "$x > 3" continue
            puts "x is $x"
        }
>>>expr {$x + 1}
<<<expr {$x + 1} <<<0,1<<<
>>>set x 1
<<<set x 1 <<<0,1<<<
>>>if {$x > 7} break
<<<if {$x > 7} break <<<0,<<<
>>>if {1 > 3} continue
<<<if {1 > 3} continue <<<0,<<<
>>>puts {x is 1}
x is 1
<<<puts {x is 1} <<<0,<<<
>>>expr {$x + 1}
<<<expr {$x + 1} <<<0,2<<<
>>>set x 2
<<<set x 2 <<<0,2<<<
>>>if {$x > 7} break
<<<if {$x > 7} break <<<0,<<<
>>>if {2 > 3} continue
<<<if {2 > 3} continue <<<0,<<<
>>>puts {x is 2}
x is 2
<<<puts {x is 2} <<<0,<<<
>>>expr {$x + 1}
<<<expr {$x + 1} <<<0,3<<<
>>>set x 3
<<<set x 3 <<<0,3<<<
>>>if {$x > 7} break
<<<if {$x > 7} break <<<0,<<<
>>>if {3 > 3} continue
<<<if {3 > 3} continue <<<0,<<<
>>>puts {x is 3}
x is 3
<<<puts {x is 3} <<<0,<<<
>>>expr {$x + 1}
<<<expr {$x + 1} <<<0,4<<<
>>>set x 4
<<<set x 4 <<<0,4<<<
>>>if {$x > 7} break
<<<if {$x > 7} break <<<0,<<<
>>>if {4 > 3} continue
>>>continue
<<<continue <<<4,<<<
<<<if {4 > 3} continue <<<4,<<<
>>>expr {$x + 1}
<<<expr {$x + 1} <<<0,5<<<
>>>set x 5
<<<set x 5 <<<0,5<<<
>>>if {$x > 7} break
<<<if {$x > 7} break <<<0,<<<
>>>if {5 > 3} continue
>>>continue
<<<continue <<<4,<<<
<<<if {5 > 3} continue <<<4,<<<
>>>expr {$x + 1}
<<<expr {$x + 1} <<<0,6<<<
>>>set x 6
<<<set x 6 <<<0,6<<<
>>>if {$x > 7} break
<<<if {$x > 7} break <<<0,<<<
>>>if {6 > 3} continue
>>>continue
<<<continue <<<4,<<<
<<<if {6 > 3} continue <<<4,<<<
>>>expr {$x + 1}
<<<expr {$x + 1} <<<0,7<<<
>>>set x 7
<<<set x 7 <<<0,7<<<
>>>if {$x > 7} break
<<<if {$x > 7} break <<<0,<<<
>>>if {7 > 3} continue
>>>continue
<<<continue <<<4,<<<
<<<if {7 > 3} continue <<<4,<<<
>>>expr {$x + 1}
<<<expr {$x + 1} <<<0,8<<<
>>>set x 8
<<<set x 8 <<<0,8<<<
>>>if {$x > 7} break
>>>break
<<<break <<<3,<<<
<<<if {$x > 7} break <<<3,<<<
<<<while {0 < 5} {
            set x [expr {$x + 1}]
            if {$x > 7} break
            if "$x > 3" continue
            puts "x is $x"
        } <<<0,<<<
>>>puts {exited second loop with X equal to 8}
exited second loop with X equal to 8
<<<puts {exited second loop with X equal to 8} <<<0,<<<
Run Code Online (Sandbox Code Playgroud)

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

  • @HaiVu我想你会发现_are_那些迭代. (2认同)