"支持你的expr表达式的例外情况"规则。发生了什么事?

时间:2016-07-09 21:43:41

标签: tcl

我在一个变量中有一个数字,比方说10,在另一个变量中有一个像+1或-2的字符串。在上述情况下,我需要评估10 + 1或10-2。

所以,我有

set foo 10
set garp -1

如果我这样做

expr $foo $garp

一切都很好(我得到9)。

啊!但一般来说,你应该在expr表达式周围加上大括号。

expr {$foo $garp}

missing operator at _@_ in expression $foo _@_$garp失败。

类似地,

expr [concat $foo $garp]

效果很好,但

expr {[concat $foo $garp]}

返回10 -1

我不想在没有真正了解正在发生的事情的情况下保留表情,因为我担心否则我或其他人会去在表达式周围放置大括号,代码将停止工作。

"正确的"这样做的方法?

2 个答案:

答案 0 :(得分:2)

一般来说,expr涉及两轮替换。

第一轮替换由命令解析器对expr命令的那些未包含在大括号中的参数执行。结果字符串被连接(通过在它们之间添加分隔符空格)到单个表达式字符串中,然后由表达式处理器对其进行解析(并稍后进行评估)。

在解析期间,表达式被分解为运算符和操作数。操作数必须与运算符分隔。假设专注于数学表达式(即丢弃字符串操作),操作数可以是以下之一:

  1. 数值
  2. 使用标准$表示法的Tcl变量。 变量的值将用作操作数。
  3. 用括号括起来的Tcl命令。 该命令将被执行,其结果将用作操作数。
  4. 带括号的子表达式,使用相同的规则进行解析。
  5. 一个数学函数,其参数是子表达式,使用相同的规则进行解析。
  6. 第2项和第3项对应于 第二轮替换 ,这是由评估期间的表达式处理程序执行的。在此步骤执行的每个替换都会产生一个可直接用于进一步评估的数值,无需重新解析和重新评估

    说完这些,让我们来看看你的例子:

    expr $foo $garp
    

    命令处理器在第一轮替换期间将此扩展为expr 10 -1,连接参数后的表达式字符串为{10 -1},表达式处理器将其解析为有效表达式{{1} }。

    10 subtract 1

    在第一轮替换期间,命令处理器将其扩展为expr [concat $foo $garp] ,从而有效地生成与前一种情况相同的表达式字符串。

    expr {10 -1}

    命令处理器保持原样,表达式处理器看到两个连续的操作数(对应上面的第2节),它们之间没有任何运算符。

    expr {$foo $garp}
    

    同样,不执行第一轮替换。解析此表达式将提取与子句3对应的单个操作数expr {[concat $foo $garp]} 。表达式处理器计算命令并将其结果(即字符串[concat $foo $garp])替换为完整表达式的结果。

    因此,表达式的正确支撑版本必须为:

    "10 -1"

    将被解析为expr {$foo + $garp}

答案 1 :(得分:0)

在这种情况下,

expr {$foo + $garp}

规则"始终支持你的表达"源于这样一个事实,即绕过参数评估步骤并将表达式字符串的评估完全保留到expr是一个好主意(因为它更安全 1 并且更有效字节码 2 )。

为了实现这一目标,传递给expr的字符串需要根据expr文档中规定的规则合法化(未支持的表达式不必合法因为论证评估步骤使其合法化)。由此可见,任何时候你需要参数评估来帮助你创建一个合法的表达式字符串是"总是支持"规则(可能还需要重新考虑代码结构 3 )。

字符串{$foo $garp}是非法的,因为变量替换只能是表达式中的操作数,这意味着我们有两个没有运算符的操作数。字符"$foo $garp"由参数evaluate转换为合法表达式,因为minus运算符被重新解释为减法运算符。

如果您有一堆成对的值,ab,并且您想添加这些值,expr $a $b可能会有用,如果您确定它们总是有一个标志。尽管如此,这很脆弱。使用其中一个

会更好
expr {$a + $b}
tcl::mathop::+ $a $b
expr [join [list $a $b] +]

(第一个是我们上面讨论过的解决方案。第二个是通过在+之外使用expr运算符来避免双重替换:变量由参数赋值器计算但是而不是命令。第三个变体具有双重替换的所有问题,并且主要是为了完整性而提及。但它仍然比expr $a $b更好。)

文档: + (operator)exprjoinlistMathematical operators as Tcl commands

1 )参数评估者,如果给出了敌对的论点,可以用例如$foo替换表达式中的[exec rm -rf *]或者Linux孩子们称之为疯狂的东西,然后是命令替换将在expr内执行。如果您通过支持表达式来禁止双重替换,则不太可能发生这种情况。

2 )字节编译器可以分析支撑表达式,并用更有效的内联计算替换对expr的调用。对于无支撑的字符串,编译器没有其他选项,只能设置对expr的调用,无论表达式是什么。

3 )看似矛盾的是,通过某种可信方法构造表达式并通过变量(set myexpr [...] ; expr $myexpr)无条件地传递它是的问题,因为这样你仍然可以完全控制表达式的内容,并且你肯定不依赖于参数评估器来为你修补它。但是,您无法获得字节码优化。