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

时间:2015-01-21 19:15:41

标签: tcl expect

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

$argv
$::argv
{*}$argv

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

param0 param1 {param 2} param3
param0 param1 {param 2} param3

传递给脚本的参数是:

param0 param1 "param 2" param3

最后一个以错误结束:

wrong # args: should be "puts ?-nonewline? ?channelId? string"
    while executing
"puts {*}$argv"

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

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"
}

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

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"
}"

评论这两个陈述表明$argv$::argv是列表:

argv IS NOT AN ARRAY
$argv IS NOT AN ARRAY
argv IS LIST
$argv IS LIST

这些列表都可以作为标准列表遍历,例如:

foreach item $argv {
  puts $item
}

foreach item $::argv {
  puts $item
}

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

wrong # args: should be "foreach varList list ?varList list ...? command"
    while executing
"foreach item {*}$argv {
  puts $item
}"

我正在使用TCL版本8.5

2 个答案:

答案 0 :(得分:3)

  

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

$argv
$::argv
{*}$argv

这里有两种不同的类型。

不合格和合格的变量

在Tcl中,不合格和合格的变量可能有点不同,但它取决于上下文(虽然非常简单)。首先,限定变量名称是指其中至少包含一个::的变量名称。如果变量名称(事件之后 $ - 在Tcl中,$只是意味着“现在读取此变量并在此处使用其内容”)以{{1}开头它是一个绝对变量名,否则合格的变量名是一个相对变量名,并根据当前命名空间进行解析(如果您不确定,可以使用::找到)。在所有上下文中,绝对变量名称始终引用相同的内容。因此,namespace current是一个绝对变量名,实际上它指的是顶级全局命名空间中名为::argv的变量。这恰好是argvtclsh将其参数写入的变量。

但是如果 no wish,则它是不合格的变量名称。如果你在程序中(或类似程序的东西,其中包括一个lambda术语,例如你用::或各种OO系统定义的方法)那么变量(大部分)被视为相对变量名称,并根据当前名称空间进行解析。 applynamespace eval是可以更改当前命名空间的两个内容(其他内容更加模糊)。如果您使用namespace code声明所有命名空间变量,则所有这些都是。否则,你可以用可变分辨率来解决一些非常讨厌的奇怪问题。所以请使用variable。真。

如果你在一个过程(类似实体)中,那个非限定名称指的是一个本地变量,当生成过程时,它的生命与堆栈中推送的堆栈帧的生命相关联。进入。这可以通过各种命令链接到其他范围(包括全局命名空间)中的变量:variableglobalupvarvariable 。但是,变量的实际分辨率是局部的。

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

正常和扩大替代

namespace upvar$argv之间的差异完全不同。

{*}$argv是正常替代。它说“在这里读取这个变量并使用它的内容”。它可以在单词的中间使用,因此$argv是一个东西,由$argv$argv$argv变量的内容串联三次组成。

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

以下是一个例子:

argv

看到区别?一个在列表中产生三个元素,另一个产生五个。用更长的东西更有意义:

set abc {a b c}
set dabcf [list d $abc f]
puts $dabcf;       # ===> “d {a b c} f”

set eabcg [list e {*}$abc g]
puts $eabcg;       # ===> “e a b c g”

随着扩展,所有Just Works™。如果没有,你就必须对set options { -foreground blue -background yellow -text "This is eye-watering stuff!" } button .b1 {*}$options -command {puts "Ouch 1"} button .b2 {*}$options -command {puts "Ouch 2"} button .b3 {*}$options -command {puts "Ouch 3"} pack .b1 .b2 .b3

做一些可怕的事情
eval

这很难做到,而且很乏味,所以它引起了很多人(包括Tcl和Tk维护者!)的许多问题,因为他们倾向于采取捷径并弄错了。为了解决这个问题,在Tcl 8.5中创建了扩展语法,以减少错误。 (简单Tcl中的典型示例往往涉及eval [list button .b1] [lrange $options 0 end] [list -command {puts "Ouch 1"}] # etc. 的事情,并且意味着很多人实际上因此而存在安全漏洞。)

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

请注意,这与变量是否合格无关。是的,这意味着如果您愿意,也可以eval

答案 1 :(得分:1)

你将替换的影响与论证扩展的影响混为一谈。

请研究Dodekalogue http://wiki.tcl.tk/10259

您将规则#5:参数扩展({*}事物)与变量替换(规则#8)混合在一起。

您在上面列出的三种表格相当于以下内容:

$argv   -> [set argv]

获取当前活动范围中的简单变量的值。

$::argv -> [namespace eval :: { set argv }] -> [set ::argv]

获取名称空间::(全局名称空间)

中变量的值
{*}$argv -> [eval [set argv]]

将变量内容扩展为多个参数。