【问题标题】:Variable substitution within braces in TclTcl中大括号内的变量替换
【发布时间】:2016-09-14 22:59:24
【问题描述】:

纠正我的错误。

当我们使用大括号内的变量时,值不会在求值过程中被替换,而只是作为参数传递给过程/命令。 (是的,有一些例外,例如expr {$x+$y})。

考虑以下场景,

场景 1

% set a 10
10
%  if {$a==10} {puts "value is $a"}
value is 10
%  if "$a==10" "puts \"value is $a\""
value is 10

场景 2

%  proc x {} {
        set c 10
        uplevel {set val $c}
}
%
% proc y {} {
         set c 10
        uplevel "set val $c"
}
% x
can't read "c": no such variable
% y
10
% set val
10
%

在这两种情况下,我们都可以看到变量替换是在if 循环体上执行的(即{puts "value is $a"}),而在uplevel 中则不是(即{set val $c}) ,基于当前上下文。

我可以看到好像他们可以通过upvar 访问它一样的东西。但是,为什么它必须在不同的地方不同?在幕后,为什么要这样设计?或者这只是Tcl 的常规工作方式?

【问题讨论】:

    标签: variables if-statement scope tcl uplevel


    【解决方案1】:

    Tcl 总是以完全相同的方式工作,只有一个解释级别,尽管在某些情况下存在第二个级别,因为命令专门请求它。它的工作方式是从不插入大括号内的内容或检查单词边界(假设这些大括号从“单词”的开头开始),插入双引号中的内容但不解析单词边界(假设它们开始一个单词),否则插值和单词边界扫描都完成(插值结果被扫描)。

    但有些命令会再次发送结果字。例如:

    eval {
        puts "this is an example with your path: $env(PATH)"
    }
    

    该规则适用于外部eval,但它会连接其参数,然后再次将结果发送到 Tcl。 if 对其主体脚本做了类似的事情,只是没有连接,而是有条件执行。 proc 也这样做,只是它会延迟运行代码,直到您调用该过程。 expr 命令类似于eval,不同之处在于将脚本发送到表达式评估引擎,这实际上是一种单独的小语言。 if 命令也使用表达式引擎(whilefor 也是如此)。表达式语言也能理解$var(和[…])。

    如果你这样做会发生什么?

    set x [expr $x + $y]
    

    好吧,首先我们解析出第一个单词set,然后解析出x,然后用第三个单词开始一个命令替换,它递归地进入解析器,直到找到匹配的]。使用内部expr,我们首先解析expr,然后解析$x(读取x 变量),然后解析+,然后解析$y。现在expr 命令使用三个参数调用;它将值与它们之间的空格连接起来,并将连接的结果发送到表达式引擎。如果您之前有 x 包含 $aby 包含 [kaboom],则要计算的表达式实际上是:

    $ab + [kaboom]
    

    这可能会给你一个关于不存在的变量或命令的错误。另一方面,如果您使用大括号执行expr {$x + $y} ,您将获得应用于两个变量内容的加法(在这种情况下仍然是一个错误, 因为两者看起来都不像数字)。


    建议您为表达式加括号,因为您编写的表达式就是要计算的表达式。否则,您可能会得到各种“意外”行为。这是一个温和的例子:

    set x {12 + 34}
    puts [expr $x]
    set y {56 + 78}
    puts [expr $y]
    puts [expr $x * $y]
    

    记住,Tcl 总是以同样的方式工作。没有特殊情况。任何看起来像特例的东西都只是实现了一种小语言的命令(通常通过递归调用回 Tcl 或表达式引擎)。

    【讨论】:

      【解决方案2】:

      除了Donal Fellows的回答:

      在场景 2 中,在 x 中调用命令 uplevel {set val $c},但由于调用者级别没有此类变量而失败。

      y 中,调用uplevel {set val 10} 的等效项(因为在解释命令时会替换c 的值)。该脚本可以在调用者级别进行评估,因为它不依赖于那里的任何变量。相反,它会在该级别创建变量 val

      之所以这样设计,是因为它为程序员提供了更多选择。如果我们想在准备执行命令时避免评估(知道我们调用的命令可能在执行时仍然评估我们的变量),我们支持我们的参数。如果我们希望在命令准备期间进行评估,我们使用双引号(或不使用引号)。

      现在试试这个:

      % set c 30
      30
      % x
      30
      % y
      10
      

      如果在调用者级别存在这样一个变量,x 是一个有用的命令,用于将变量val 设置为c 的值,而y 是一个有用的命令用于将变量val 设置为封装在y 中的值的命令。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-14
        • 2016-11-08
        • 1970-01-01
        相关资源
        最近更新 更多