以下变量之间有什么区别:
$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
答案 0 :(得分:3)
以下变量之间有什么区别:
$argv $::argv {*}$argv
这里有两种不同的类型。
在Tcl中,不合格和合格的变量可能有点不同,但它取决于上下文(虽然非常简单)。首先,限定变量名称是指其中至少包含一个::
的变量名称。如果变量名称(事件之后 $
- 在Tcl中,$
只是意味着“现在读取此变量并在此处使用其内容”)以{{1}开头它是一个绝对变量名,否则合格的变量名是一个相对变量名,并根据当前命名空间进行解析(如果您不确定,可以使用::
找到)。在所有上下文中,绝对变量名称始终引用相同的内容。因此,namespace current
是一个绝对变量名,实际上它指的是顶级全局命名空间中名为::argv
的变量。这恰好是argv
和tclsh
将其参数写入的变量。
但是如果 no wish
,则它是不合格的变量名称。如果你在程序中不(或类似程序的东西,其中包括一个lambda术语,例如你用::
或各种OO系统定义的方法)那么变量(大部分)被视为相对变量名称,并根据当前名称空间进行解析。 apply
和namespace eval
是可以更改当前命名空间的两个内容(其他内容更加模糊)。如果您使用namespace code
声明所有命名空间变量,则所有这些都是。否则,你可以用可变分辨率来解决一些非常讨厌的奇怪问题。所以请使用variable
。真。
如果你在一个过程(类似实体)中,那个非限定名称指的是一个本地变量,当生成过程时,它的生命与堆栈中推送的堆栈帧的生命相关联。进入。这可以通过各种命令链接到其他范围(包括全局命名空间)中的变量:variable
,global
,upvar
和variable
。但是,变量的实际分辨率是局部的。
最后,可能还有一个自定义变量解析器。由于您使用的是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]]
将变量内容扩展为多个参数。