TCL/Expect - $argv VS $::argv VS {*}$argv

Wak*_*nka 2 tcl expect

以下变量之间有什么区别:

$argv
$::argv
{*}$argv
Run Code Online (Sandbox Code Playgroud)

前两个可以通过puts命令打印,它们返回以下输出:

param0 param1 {param 2} param3
param0 param1 {param 2} param3
Run Code Online (Sandbox Code Playgroud)

传递给脚本的参数是:

param0 param1 "param 2" param3
Run Code Online (Sandbox Code Playgroud)

最后一个结果出现错误:

wrong # args: should be "puts ?-nonewline? ?channelId? string"
    while executing
"puts {*}$argv"
Run Code Online (Sandbox Code Playgroud)

我使用以下代码在该领域做了一些研究:

if {[array exists $argv]} {
  puts "\$argv IS ARRAY"
} else {
  puts "\$argv IS NOT AN ARRAY"
}

if {[string is list $argv]} {
  puts "\$argv IS LIST"
} else {
  puts "\$argv IS NOT LIST"
}

if {[array exists $::argv]} {
  puts "\$::argv IS ARRAY"
} else {
  puts "\$::argv IS NOT AN ARRAY"
}

if {[string is list $::argv]} {
  puts "\$::argv IS LIST"
} else {
  puts "\$::argv IS NOT LIST"
}

if {[array exists {*}$argv]} {
  puts "{*}\$::argv IS ARRAY"
} else {
  puts "{*}\$::argv IS NOT AN ARRAY"
}

if {[string is list {*}$argv]} {
  puts "{*}\$::argv IS LIST"
} else {
  puts "{*}\$::argv IS NOT LIST"
}
Run Code Online (Sandbox Code Playgroud)

最后两个 if-else 语句包含{*}$argv以下错误:

wrong # args: should be "array exists arrayName"
    while executing
"array exists {*}$argv"
    invoked from within
"if {[array exists {*}$argv]} {
  puts "{*}\$::argv IS ARRAY"
} else {
  puts "{*}\$::argv IS NOT AN ARRAY"
}"
Run Code Online (Sandbox Code Playgroud)

注释掉这两个语句表明$argv$::argv是列表:

argv IS NOT AN ARRAY
$argv IS NOT AN ARRAY
argv IS LIST
$argv IS LIST
Run Code Online (Sandbox Code Playgroud)

这两个列表都可以作为标准列表进行遍历,例如:

foreach item $argv {
  puts $item
}
Run Code Online (Sandbox Code Playgroud)

或者

foreach item $::argv {
  puts $item
}
Run Code Online (Sandbox Code Playgroud)

尝试以相同的方式遍历{*}$argv会再次导致以下错误:

wrong # args: should be "foreach varList list ?varList list ...? command"
    while executing
"foreach item {*}$argv {
  puts $item
}"
Run Code Online (Sandbox Code Playgroud)

我用的是TCL 8.5版本

Don*_*ows 5

\n

以下变量之间有什么区别:

\n
$argv\n$::argv\n{*}$argv\n
Run Code Online (Sandbox Code Playgroud)\n
\n

这里有两种类型的差异。

\n

不合格和合格变量

\n

在 Tcl 中,非限定变量和限定变量可能有点不同,但这取决于上下文(尽管以一种非常简单的方式)。::首先,限定变量名是指其中至少包含一个的变量名。如果变量名(Tcl 中 \xe2\x80\x94 后面的内容,仅表示\xe2\x80\x9cread 这个变量并在此处使用其内容\xe2\x80\x9d)以 开头,则它是绝对变量名,否则限定变量名是相对变量名,并根据当前命名空间进行解析(如果不确定,可以使用该命名空间来查找)。在所有上下文中,绝对变量名始终指代相同的事物。因此,是一个绝对变量名,实际上它指的是在顶级全局命名空间中调用的变量。这恰好是一个变量,可以将它们的参数写入其中。$$::namespace current::argvargvtclshwish

\n

但如果没有 ::则它是一个非限定变量名。如果您不在过程(或类似过程的事物中,其中包括 lambda 术语,例如您使用的apply或各种 OO 系统定义的方法),则该变量(大部分)将被视为相对变量名称并根据当前名称空间进行解析。namespace evalnamespace code是可以更改当前名称空间的两个内容(其他内容更加晦涩难懂)。所有这些都供您用来variable声明所有名称空间变量。否则,您可能会遇到一些非常令人讨厌的可变分辨率的奇怪问题。所以一定要使用variable. 真的。

\n

但是,如果您处于过程(类似实体)中,则该非限定名称指的是局部变量,其生命周期与进入过程时压入堆栈的堆栈帧的生命周期耦合。可以通过各种命令链接到其他范围(包括全局命名空间)中的变量:globalupvarvariablenamespace upvar。然而,变量的实际分辨率是局部的。

\n

最后,可能还存在一个自定义变量解析器。由于您使用的是 Tcl 8.5,因此您最有可能看到此功能的地方是使用 Incr Tcl(Tcl 的对象系统)。自定义变量解析器可以做一些复杂的事情。(如果您使用的是 Tcl 8.6,最有可能在工作中看到自定义变量解析器的地方是 TclOO。那里的变量解析器非常保守和谨慎,但允许将局部变量绑定到对象变量,而无需显式声明此变量在每个方法中)。

\n

正常替代和扩展替代

\n

$argv和之间的区别{*}$argv是完全不同的。

\n

$argv是正常的替换。它说\xe2\x80\x9cread这里变量并使用它的内容代替\xe2\x80\x9d。它可以用在单词的中间,$argv$argv$argv一个事物也是如此,由变量的内容串联argv三次组成。

\n

{*},当放置在单词的开头时(在其他地方并不特殊),标记该单词以进行扩展。当一个单词被扩展时,在完成所有其他正常替换后,它会被解析为 Tcl 列表,并且该列表中的单词将用作正在构建的结果命令中的单词。{*}$argv是一种退化情况,其中单词的其余部分只是从变量中读取的内容;命令中使用的单词是变量中列表的元素argv。因为这通常是一个列表,所以这一切都很顺利。

\n

这是一个例子:

\n
set abc {a b c}\nset dabcf [list d $abc f]\nputs $dabcf;       # ===> \xe2\x80\x9cd {a b c} f\xe2\x80\x9d\n\nset eabcg [list e {*}$abc g]\nputs $eabcg;       # ===> \xe2\x80\x9ce a b c g\xe2\x80\x9d\n
Run Code Online (Sandbox Code Playgroud)\n

看到不同?一个在列表中生成三个元素,另一个生成五个。如果有更长的东西就更有意义了:

\n
set options {\n    -foreground blue\n    -background yellow\n    -text "This is eye-watering stuff!"\n}\nbutton .b1 {*}$options -command {puts "Ouch 1"}\nbutton .b2 {*}$options -command {puts "Ouch 2"}\nbutton .b3 {*}$options -command {puts "Ouch 3"}\npack .b1 .b2 .b3\n
Run Code Online (Sandbox Code Playgroud)\n

通过扩展,一切都可以正常工作\xe2\x84\xa2。如果没有,你就必须做一些可怕的事情eval

\n
eval [list button .b1] [lrange $options 0 end] [list -command {puts "Ouch 1"}]\n# etc.\n
Run Code Online (Sandbox Code Playgroud)\n

这很难做到正确,而且很乏味,所以它给很多人(包括 Tcl 和 Tk 维护者!)带来了很多问题,因为他们倾向于走捷径并出错。为了解决这个问题,Tcl 8.5 中创建了扩展语法,以减少所有错误的发生。(普通 Tcl 中的原型示例往往涉及 的内容exec,并且意味着很多人实际上因此而存在安全漏洞。)

\n

作为奖励,使用{*}比使用快得多,eval因为扩展可以保证它永远不会对事物进行复杂的重新分析。在 Tcl 中,更快几乎总是与更安全相关。

\n

请注意,这与变量是否合格无关。是的,这意味着{*}$::argv如果您愿意,您也可以拥有。

\n