首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >TCL/Expect - $argv VS $::argv VS {*}$argv

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

Stack Overflow用户
提问于 2015-01-22 03:15:41
回答 2查看 2.1K关注 0票数 1

以下变量之间的区别是什么:

代码语言:javascript
复制
$argv
$::argv
{*}$argv

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

代码语言:javascript
复制
param0 param1 {param 2} param3
param0 param1 {param 2} param3

传递给脚本的参数是:

代码语言:javascript
复制
param0 param1 "param 2" param3

最后一个以错误结束:

代码语言:javascript
复制
wrong # args: should be "puts ?-nonewline? ?channelId? string"
    while executing
"puts {*}$argv"

我在这方面做了一些研究,使用了以下代码:

代码语言:javascript
复制
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语句以以下错误结束:

代码语言:javascript
复制
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是列表:

代码语言:javascript
复制
argv IS NOT AN ARRAY
$argv IS NOT AN ARRAY
argv IS LIST
$argv IS LIST

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

代码语言:javascript
复制
foreach item $argv {
  puts $item
}

代码语言:javascript
复制
foreach item $::argv {
  puts $item
}

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

代码语言:javascript
复制
wrong # args: should be "foreach varList list ?varList list ...? command"
    while executing
"foreach item {*}$argv {
  puts $item
}"

我使用的是TCL 8.5版

EN

回答 2

Stack Overflow用户

发布于 2015-01-22 05:17:12

以下变量之间的区别:

$argv $::argv {*}$argv

这里有两种类型的区别。

非限定变量和限定变量

在Tcl中,非限定变量和限定变量可能略有不同,但这取决于上下文(尽管以一种非常简单的方式)。首先,限定变量名中至少包含一个::。如果变量名(在$ - in Tcl中,$的意思是“立即读取此变量并在此处使用其内容”)以::开头,则它是绝对变量名,否则限定变量名是相对变量名,并相对于当前名称空间进行解析(如果您不确定,可以使用namespace current找到)。在所有上下文中,绝对变量名总是引用相同的东西。因此,::argv是一个绝对变量名,实际上它引用了顶级全局名称空间中的一个名为argv的变量。这恰好是tclshwish向其中写入参数的变量。

但是如果没有::,它就是一个非限定变量名。如果您不在过程中(或类似过程的东西,其中包括一个lambda术语,如您将在apply中使用的术语或由各种OO系统定义的方法),那么变量(主要)将被视为一个相对变量名,并相对于当前名称空间进行解析。namespace evalnamespace code是可以改变当前名称空间的两个东西(其他的比较模糊)。所有这些都提供给您使用variable来声明所有的名称空间变量。否则,你可能会遇到一些奇怪的变量分辨率问题,这真的很糟糕。所以一定要使用variable。真的。

但是,如果您在一个过程(-like实体)中,非限定名称指的是一个局部变量,它的生命周期与进入该过程时压入堆栈的堆栈框架的生命周期相耦合。它可以通过各种命令链接到其他作用域(包括全局命名空间)中的变量:globalupvarvariablenamespace upvar。但是,变量的实际解析是局部的。

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

规范替换和扩展替换

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

$argv是一个正常的替换。它说“在这里读这个变量,并使用它的内容”。它可以用在单词的中间,所以$argv$argv$argv是一个东西,由argv变量的内容连接三次组成。

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

下面是一个例子:

代码语言:javascript
复制
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”

看到区别了吗?一个生成列表中的三个元素,另一个生成五个元素。对于有些更长的东西,它甚至更有意义:

代码语言:javascript
复制
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做一些可怕的事情

代码语言:javascript
复制
eval [list button .b1] [lrange $options 0 end] [list -command {puts "Ouch 1"}]
# etc.

这很难做对,而且很乏味,所以它引起了很多人(包括Tcl和Tk维护者!)许多问题是因为他们倾向于走捷径并搞错。正是为了解决这个问题,在TCL8.5中创建了扩展语法,以使所有这些都不容易出错。(简单的Tcl中的原型示例往往涉及到exec,这意味着相当多的人实际上因此存在安全漏洞。)

作为一个额外的好处,使用{*}比使用eval快得多,因为扩展可以保证它永远不会进行复杂的重新解析。在Tcl中,更快几乎总是与更安全相关。

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

票数 4
EN

Stack Overflow用户

发布于 2015-01-22 04:59:20

您混淆了替换的效果和参数扩展的效果。

请学习Dodekalogue http://wiki.tcl.tk/10259

你混合了规则#5:参数扩展( {*} )和变量替换(规则#8)。

上面列出的三个表单相当于以下三个表单:

代码语言:javascript
复制
$argv   -> [set argv]

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

代码语言:javascript
复制
$::argv -> [namespace eval :: { set argv }] -> [set ::argv]

获取名称空间:: (全局名称空间)中变量的值

代码语言:javascript
复制
{*}$argv -> [eval [set argv]]

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28075027

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档