Tcl 中大括号内的变量替换
Variable substitution within braces in Tcl
不对的地方指正。
当我们在大括号内使用变量时,值不会在计算期间被替换,而只是作为参数传递给 procedure/command。 (是的,有一些例外,比如 expr {$x+$y}
)。
考虑以下场景,
场景一
% set a 10
10
% if {$a==10} {puts "value is $a"}
value is 10
% if "$a==10" "puts \"value is $a\""
value is 10
场景二
% 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
工作的常规方式?
Tcl 总是以完全相同的方式工作,只有一个级别的解释,尽管在某些情况下有第二个级别,因为命令特别要求它。它的工作方式是大括号内的内容 never 插值或检查单词边界(前提是这些大括号从“单词”的开头开始),双引号中的内容被插值但不是解析单词边界(前提是它们以单词开头),否则会完成插值和单词边界扫描(插值结果 未 扫描)。
但是有些命令会再次发送生成的单词。例如:
eval {
puts "this is an example with your path: $env(PATH)"
}
该规则适用于外部 eval
,但它会连接其参数,然后再次将结果发送到 Tcl。 if
与其主体脚本执行类似的操作,只是没有连接,而是有条件执行。 proc
也做同样的事情,除了它会延迟 运行 代码直到您调用该过程。 expr
命令类似于 eval
,除了将脚本发送到表达式评估引擎,这实际上是一种独立的小语言。 if
命令也使用表达式引擎(while
和 for
也是如此)。表达式语言也可以理解 $var
(和 […]
)。
如果你这样做会发生什么?
set x [expr $x + $y]
嗯,首先我们解析出第一个单词,set
,然后x
,然后用第三个单词开始命令替换,递归进入解析器直到匹配[=26] =] 被发现。对于内部的 expr
,我们首先解析 expr
,然后是 $x
(读取 x
变量),然后是 +
,然后是 $y
。现在使用三个参数调用 expr
命令;它将值与它们之间的空格连接起来,并将 连接的结果 发送到表达式引擎。如果 x
之前包含 $ab
并且 y
包含 [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 或表达式引擎)。
另外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
.
中的值
不对的地方指正。
当我们在大括号内使用变量时,值不会在计算期间被替换,而只是作为参数传递给 procedure/command。 (是的,有一些例外,比如 expr {$x+$y}
)。
考虑以下场景,
场景一
% set a 10
10
% if {$a==10} {puts "value is $a"}
value is 10
% if "$a==10" "puts \"value is $a\""
value is 10
场景二
% 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
工作的常规方式?
Tcl 总是以完全相同的方式工作,只有一个级别的解释,尽管在某些情况下有第二个级别,因为命令特别要求它。它的工作方式是大括号内的内容 never 插值或检查单词边界(前提是这些大括号从“单词”的开头开始),双引号中的内容被插值但不是解析单词边界(前提是它们以单词开头),否则会完成插值和单词边界扫描(插值结果 未 扫描)。
但是有些命令会再次发送生成的单词。例如:
eval {
puts "this is an example with your path: $env(PATH)"
}
该规则适用于外部 eval
,但它会连接其参数,然后再次将结果发送到 Tcl。 if
与其主体脚本执行类似的操作,只是没有连接,而是有条件执行。 proc
也做同样的事情,除了它会延迟 运行 代码直到您调用该过程。 expr
命令类似于 eval
,除了将脚本发送到表达式评估引擎,这实际上是一种独立的小语言。 if
命令也使用表达式引擎(while
和 for
也是如此)。表达式语言也可以理解 $var
(和 […]
)。
如果你这样做会发生什么?
set x [expr $x + $y]
嗯,首先我们解析出第一个单词,set
,然后x
,然后用第三个单词开始命令替换,递归进入解析器直到匹配[=26] =] 被发现。对于内部的 expr
,我们首先解析 expr
,然后是 $x
(读取 x
变量),然后是 +
,然后是 $y
。现在使用三个参数调用 expr
命令;它将值与它们之间的空格连接起来,并将 连接的结果 发送到表达式引擎。如果 x
之前包含 $ab
并且 y
包含 [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 或表达式引擎)。
另外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
.