Tcl中括号内的变量替换

时间:2016-05-18 12:38:41

标签: variables if-statement scope tcl uplevel

在我错的地方纠正我。

当我们在大括号内使用变量时,在评估期间不会替换该值,只是作为过程/命令的参数传递。 (是的,有一些例外,例如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如何运作的传统方式?

2 个答案:

答案 0 :(得分:3)

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或表达式引擎)。

答案 1 :(得分:0)

除了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内封装的值。