为什么花括号允许变量替换?

Why curly braces allow variable substitution?

Tcl 手册说大括号不允许变量替换。 然而,这仅适用于某些命令,而不适用于其他命令。

有什么区别以及如何识别将发生替换的情况和不会发生替换的情况?

% set x 3
3
% puts {$x}
$x
% expr {$x}
3

参考 standard commands 的列表:任何接受 "body" 或 "script" 参数的命令最终都会将该主体作为代码求值。无法保证详尽无遗:

after, apply, catch, eval, expr, fileevent (and chan event), for, foreach, if, interp eval, lmap, some namespace subcommands, some oo::* commands, proc, subst, switch, try, uplevel, while

这确实是 Tcl 最大的优势之一。它使您能够轻松编写自己的控制结构。例如,Tcl 不提供 do-while 循环,但你可以这样做:

proc do {body while condition} {
    if {$while ni {while until}} {
        error "some message about usage..."
    }
    while true {
        uplevel 1 $body
        set status [uplevel 1 [list expr $condition]]
        if {$while eq "while" && !$status} then break
        if {$while eq "until" &&  $status} then break
    }
}

所以

% set i 0; while {[incr i] < 3} {puts "$i"}
1
2
% set i 0; do {puts "$i"} while {[incr i] < 3}
0
1
2
% set i 0; do {puts "$i"} until {[incr i] == 3}
0
1
2

某些命令明确描述为将一个或多个参数视为脚本或表达式;当脚本或表达式的评估发生时(可能立即,也可能稍后,具体取决于命令),执行该字符串(即脚本或表达式)中描述的替换。 (subst 命令是一种特殊情况,它只能应用选定的替换子集。)

你怎么知道哪个是哪个? 这取决于命令。 字面意思。去阅读文档。例如,在 the documentation for catch 中我们看到:

SYNOPSIS

catch script ?resultVarName? ?optionsVarName?

DESCRIPTION

The catch command may be used to prevent errors from aborting command interpretation. The catch command calls the Tcl interpreter recursively to execute script, and always returns without raising an error, regardless of any errors that might occur while executing script. […]

在这种情况下,我们看到第一个参数总是通过调用 Tcl 解释器(或者更确切地说,它实际上是在大多数情况下编译的字节码,但这是一个实现细节)作为 Tcl 脚本(立即)求值。

同样,在the documentation for proc中我们看到:

SYNOPSIS

proc name args body

DESCRIPTION

The proc command creates a new Tcl procedure named name, replacing any existing command or procedure there may have been by that name. Whenever the new command is invoked, the contents of body will be executed by the Tcl interpreter. […]

在这种情况下,body 将被评估为脚本(“由 Tcl 解释器”是一种语言形式,意味着)但稍后,当程序被调用时。 (catch 对此只字不提;言下之意,它会立即采取行动。)

第三种情况是the documentation for while:

SYNOPSIS

while test body

DESCRIPTION

The while command evaluates test as an expression (in the same way that expr evaluates its argument). The value of the expression must a proper boolean value; if it is a true value then body is executed by passing it to the Tcl interpreter. […]

由此可见,test参数是一个表达式(使用表达式规则),body是一个脚本。


如果您想创建一个无替换的单命令脚本,您可以在其中对所有内容使用任意值(这非常适合设置回调)使用list命令定义为以规范形式生成列表,这恰好(通过设计)恰好是没有替换惊喜的单个命令可以采用的形式:

set xyz "123 456"
set callback [list puts $xyz]
set xyz {[crash bang wallop]}
puts "READY..."
eval $callback