tcl 不带括号的表达式的问题

示例

将表达式字符串参数作为大括号字符串提供是一种好习惯。标题“双重替代”概述了背后的重要原因。

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

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

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

命令if,for和while使用相同的求值器代码作为条件参数:

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!

在这里,我们遇到了带有括号的参数的麻烦:当评估器expr执行替换时,表达式字符串已被解析为运算符和操作数,因此评估者看到的是一个由两个操作数组成的字符串,它们之间没有运算符。(错误消息是“ missing operator at _@_ in expression "$a _@_$b"”。)

在这种情况下,expr调用之前的变量替换可以防止错误。支撑参数可以防止变量替换,直到表达式求值,这会导致错误。

可能会发生这种情况,最典型的情况是将要求值的表达式作为变量或参数传入。在这些情况下,除了保留参数不加括号之外,没有其他选择,允许参数评估程序“解包”表达式字符串以传递给expr。

但是,在大多数其他情况下,将表达式括起来并没有害处,并且确实可以避免很多问题。一些例子:

双重替代

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

无括号的形式将执行命令替换,该命令替换会以某种方式破坏计算机(或加密或格式化硬盘或您所拥有的东西)。大括号形式将执行变量替换,然后尝试(失败)创建字符串“ [exec make computer go boom]”。避免了灾难。

无休止的循环

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

此问题同时影响for和while。尽管看起来该循环将倒数至0并退出,但while的条件参数实际上始终10>0是因为while激活命令时该参数被评估为该参数。将参数括起来后,它将作为传递给while命令$i>0,并且该变量将为每次迭代替换一次。使用此代替:

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

总体评价

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

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

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

定义了多个运算符(逻辑连接词||和&&,以及条件运算符?:)以求值其所有操作数,但只有在表达式字符串加括号时它们才能按设计工作。