我刚刚开始在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"}]
或好像(这是一个字符串,因为它本身只是一个字符串,它是真的。)
它是如何工作的?给我机械师。巨大的偏头痛试图解决这个问题。
答案 0 :(得分:5)
Tcl有一个非常简单的评估模型。它总是在看到它们的时候评估它们;如果一个单词在大括号中,则它不会被取消,但除此之外它有$
和[…]
(以及\
,但您不会使用这些替换; "…"
只是将一堆字符组合成一个单词(即,它会覆盖通常的空格分隔规则)。一旦它组装了单词,它就会将它们发送到命令实现(从第一个单词查找)以便执行。那就是它。
您建议始终支持您的表达式,因为这可以避免混淆(并使Tcl能够编译代码)。
set x 0
没有替换。三个词。 set
将0
写入变量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
。
这些与迭代4非常相似。
请注意,"…"
仅在到达时进行评估。这意味着对于循环内部的循环,它们每次循环都会被评估/替换。但是,{<1}}的循环条件是不每次都被重新替换,因为在调用命令之前发生了替换。 (它与表达式有点混淆,因为它们也支持while
和$
以及[…]
的类似Tcl的语法,但它们&#39;正式地使用单独的解析器进行单独的语言。)
第一行的"…"
/ 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
。
我注意到您使用双引号而不是while
和if
条件的大括号。请使用大括号,如:
while {$x < 5} { ... }
同样地:
if {$x > 3} { ... }
答案 2 :(得分:0)
如果表达式/ test / condition未包含在大括号内,则在while命令开始执行之前将进行变量替换,这意味着循环体所做的变量更改将不会在表达式中被考虑。