15

我对以下代码的 tcl 中的 if 语句有疑问:

if {(($number == 1)&&($name == "hello")) || (($number == 0)&&($name == "yes"))} {
    #do something here
}

上面的代码有效,但如果我这样写:

if {{$number == 1 && $name == "hello"} || {$number == 0&&$name == "yes"}} {
    #do something here
}

它抱怨应该$number是一个布尔值,为什么?第二个不是有效的表达吗?如何纠正它?

4

4 回答 4

36

大括号{}, 和圆括号 ,在 Tcl()不可互换。

形式上,大括号(除了一个例外)是一种引用,表示不再对内容执行替换。在上面的第一种情况下,这意味着该参数被传递给if而没有替换,它将它作为一个表达式来计算。表达式子语言具有与一般 Tcl 高度相似的大括号解释方案;它们表示一个文字值,无需对其执行进一步的替换。

相比之下,括号在 Tcl 中大多不是特殊的。例外情况是数组元素的名称(例如$foo(bar),其他一些东西)。在 Tcl 中使用括号(无论是否平衡)作为命令名的一部分是完全合法的,但你可能会让你的程序员同事抱怨你写了令人困惑的代码。

细节

在这种特定情况下, this 的测试表达式if

if {{$number == 1 && $name == "hello"} || {$number == 0&&$name == "yes"}} {...}

被解析为:

废话#1 LOGICAL_OR 废话#2

其中每个blah都是文字。不幸的是,blah#1(完全等于$number == 1 && $name == "hello")没有布尔解释。(也没有,blah#2但我们从不考虑这一点。)这里的事情肯定是非常错误的!

最简单的解决方法是将那些虚假的大括号改回括号:

if {($number == 1 && $name == "hello") || ($number == 0&&$name == "yes")} {...}

我敢打赌这就是你最初想要的。

警告:高级主题

但是,另一个解决方法是添加一些额外的内容:

if {[expr {$number == 1 && $name == "hello"}] || [expr {$number == 0&&$name == "yes"}]} {...}

这通常不是一个好主意——额外的体积没有额外的收益——但在你试图使用动态生成的表达式作为测试条件的地方是有意义的。除非你真的很确定你需要这样做,否则不要这样做我是认真的。这是一种非常先进的技术,您几乎不需要,而且通常有更好的方法来实现您的总体目标。如果您认为您可能需要它,看在上帝的份上,请在 SO 上提问,我们将尝试找到更好的方法;几乎总是有一个可用的。

于 2012-06-29T12:55:53.360 回答
1

我认为您收到的错误消息并不意味着它$number必须是布尔值(我收到了消息expected boolean value but got "$number == 1 && $name == "hello"")。这意味着字符串$number == 1 && $name == "hello"不是布尔值 - 这绝对是真的。如果您在表达式中使用花括号,if则不会对这些字符串进行评估,而是将其简单地解释为 - 作为字符串。

于 2012-06-29T12:29:31.297 回答
1

简而言之:if为其条件脚本使用一种特殊的“迷你语言”——与exprcommand理解的相同。这在if手册页中有说明:

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

与非常类似 LISP 和/或类似 Unix shell 的 Tcl 本身相比,“expr迷你语言”在感觉上更像 C 的意义上更“传统”。

于 2012-06-29T16:09:03.537 回答
1

($number == 1)数字中分配 1 并进行比较。例如:1==1这里的输出是布尔值。但是在{$number == 1 && $name == "hello"}$number 中没有分配,因为花括号$number是比较的,1所以得到的输出不是布尔值。

于 2014-12-11T06:54:44.707 回答