无支撑表达式的问题

将表达式字符串参数作为支撑字符串提供是一种很好的做法。 双重替代标题概述了背后的重要原因。

expr 命令评估基于运算符的表达式字符串以计算值。此字符串由调用中的参数构造。

expr 1 + 2    ; # three arguments
expr "1 + 2"  ; # one argument
expr {1 + 2}  ; # one argument

这三个调用是等效的,表达式字符串是相同的。

命令 ifforwhile 对其条件参数使用相同的求值程序代码:

if {$x > 0} ...
for ... {$x > 0} ... ...
while {$x > 0} ...

主要区别在于条件表达式字符串必须始终是单个参数。

与 Tcl 中命令调用中的每个参数一样,内容可能会也可能不会被替换,具体取决于它们如何被引用/转义:

set a 1
set b 2
expr $a + $b   ; # expression string is {1 + 2}
expr "$a + $b" ; # expression string is {1 + 2}
expr \$a + \$b ; # expression string is {$a + $b}
expr {$a + $b} ; # expression string is {$a + $b}

第三和第四种情况存在差异,因为反斜杠/支撑会阻止替换。结果仍然相同,因为 expr 中的赋值器本身可以执行 Tcl 变量替换并将字符串转换为 {1 + 2}

set a 1
set b "+ 2"
expr $a $b   ; # expression string is {1 + 2}
expr "$a $b" ; # expression string is {1 + 2}
expr {$a $b} ; # expression string is {$a $b}: FAIL!

这里我们遇到了 braced 参数的问题:当 expr 中的求值程序执行替换时,表达式字符串已经被解析为运算符和操作数,因此求值程序看到的是一个由两个操作数组成的字符串,它们之间没有运算符。 (错误消息是“missing operator at _@_ in expression "$a _@_$b"”。)

在这种情况下,调用 expr 之前的变量替换可以防止错误。支持参数阻止了变量替换,直到表达式评估,这导致了错误。

这种情况可能会发生,最常见的情况是将要评估的表达式作为变量或参数传入。在这些情况下,除了保持参数 unbraced 以允许参数赋值器解包表达式字符串以传递给 expr 之外别无选择。

但在大多数其他情况下,支持表达并没有什么坏处,实际上可以避免很多问题。一些例子:

双重替代

set a {[exec make computer go boom]}
expr $a      ; # expression string is {[exec make computer go boom]}
expr {$a}    ; # expression string is {$a}

unbraced 表单将执行命令替换,这是一个以某种方式销毁计算机的命令(或加密或格式化硬盘,或你有什么)。支撑形式将执行变量替换,然后尝试(并且失败)来创建字符串“[exec make computer go boom]”。避免灾难。

无尽的循环

set i 10
while "$i > 0" {puts [incr i -1]}

这个问题影响了 forwhile。虽然看起来这个循环会倒计数到 0 并退出,但 while 的条件参数实际上总是 10>0,因为这是在 while 命令被激活时被评估的参数。当参数被支撑时,它将作为 $i>0 传递给 while 命令,并且变量将在每次迭代时被替换一次。改为使用它:

while {$i > 0} {puts [incr i -1]}

总评估

set a 1
if "$a == 0 && [incr a]" {puts abc}

运行此代码后 a 的价值是多少?由于 && 运算符仅在左操作数为真时才计算右操作数,因此该值仍应为 1.但实际上,它是 2.这是因为在表达式字符串为时,参数赋值器已经执行了所有变量和命令替换评估。改为使用它:

if {$a == 0 && [incr a]} {puts abc}

几个运算符(逻辑连接词||&&,以及条件运算符 ?:)被定义为评估它们的所有操作数,但它们只能在表达式字符串被支撑的情况下按设计工作。