如何在tcl中将变量参数从一个函数传递到另一个函数
我想将在一个函数中获得的变量参数传递给另一个函数,但我不能这样做。函数获取偶数个变量参数,然后必须在数组中进行转换。下面是一个例子 过程如何在tcl中将变量参数从一个函数传递到另一个函数,tcl,variadic-functions,Tcl,Variadic Functions,我想将在一个函数中获得的变量参数传递给另一个函数,但我不能这样做。函数获取偶数个变量参数,然后必须在数组中进行转换。下面是一个例子 过程abc1获取两个参数(kk),而不是形式abc1过程这些参数必须传递到过程abc,其中列表到数组的转换必须完成。列表到数组的转换在程序1中有效,即abc1,但在第二个程序中无效,即abc 获得的误差如下所示 proc abc {args} { puts "$args" array set arg $args } proc abc1 {args}
abc1
获取两个参数(kk
),而不是形式abc1
过程这些参数必须传递到过程abc
,其中列表到数组的转换必须完成。列表到数组的转换在程序1中有效,即abc1
,但在第二个程序中无效,即abc
获得的误差如下所示
proc abc {args} {
puts "$args"
array set arg $args
}
proc abc1 {args} {
puts "$args"
array set arg $args
set l2 [array get arg]
abc $l2
}
abc1 k k
abc k k
输出:
k k
{k k}
list must have an even number of elements
while executing
"array set arg $l1"
(procedure "abc" line 8)
invoked from within
"abc $l2"
(procedure "abc1" line 5)
invoked from within
"abc1 k k"
(file "vfunction.tcl" line 18)
当您传递
abc$l2
时,您正在将abc1的args
作为单个参数传递给abc
。因此在abc
args
中,保存了一个包含单个项的列表({k}
)
您可以这样做:
proc abc {args} {
#the next line assumes that if $args has only a single item, then it is
#a list in itself. It's a risky decision to make but should work here.
if { [llength $args] == 1 } { set args [lindex $args 0] }
puts "$args"
array set arg $args
}
两者之间有很大的区别
abc k k
及
在第一种情况下,传递两个参数,每个参数都是k
。在第二种情况下,您传递的是一个事物列表—在您的示例中,是一个包含两个k的列表:kk
Nir有意绕过这个问题,但更好的解决方案是编写abc1
,以便正确调用abc
:
proc abc1 {args} {
array set arg $args
set l2 [array get arg]
eval abc $l2
# or just
# eval abc $args
}
最佳解决方案:扩展替换
正确的方法是确保外部过程(在堆栈方面)正确调用内部过程;如果需要多个参数,则应提供多个参数。随着Tcl 8.5的出现,这只需使用一种称为扩展替换的小语言语法即可实现:
proc abc1 {args} {
puts "$args"
array set arg $args
set l2 [array get arg]
abc {*}$l2
# Or combine the two lines above into: abc {*}[array get arg]
}
所做的只是说单词的其余部分应该被分解(使用列表语法规则)并用作多个参数,而不是Tcl默认的“一个可视单词形成一个单词”规则。这是理想的选择
旧的解决方案:Eval命令
如果出于某种原因(例如,Tcl 8.4或更早版本),您仍在使用旧版本的Tcl,则使用eval
命令,而不是上述语法:
eval abc $l2
对于上面的eval
,您可能会在旧代码中看到一些更有效的方法;例如:
eval [linsert $l2 0 abc]
eval [list abc] [lrange $l2 0 end]
# ... etc ...
但是实际上,它们都被abc{*}$l2所淘汰了,因为它更短、更简单、更快。(它只是在8.4或之前版本中不可用,而且有太多的部署尚未升级。)如果可以,请使用扩展语法。事实上,8.5及更高版本的惯用Tcl代码几乎不需要
eval
;这被证明是正确的程度甚至让语言的维护人员感到相当惊讶。堆栈跟踪中的行号是错误的;这可能是因为我重新格式化了,也可能是因为在发布之前进行了蒸馏。不管怎样,这对问题都不是很重要。这是一份由用户“angle”提交的答案的逐字副本(随后被删除)。你是从两个不同的账户中回答,还是抄袭了其他人的答案?首先,我报告的“angle”是垃圾邮件,因为他的答案是我答案的逐字逐句的副本(而不是相反,你可以看看时间。第二,关于“有更好的方法解决这个问题”,我不确定:“eval”在我看来,这个选项并不安全,扩展替换只在8.5中出现。如果你能想出一个更好的解决方案,你应该发布它。另一个可能比这两个更好的选择是通过引用传递参数并使用upvar
访问它们,而不是{*}
或eval
。
eval [linsert $l2 0 abc]
eval [list abc] [lrange $l2 0 end]
# ... etc ...