如何在tcl中将变量参数从一个函数传递给另一个函数

时间:2010-12-30 06:59:46

标签: tcl variadic-functions

我想将在一个函数中获得的变量参数传递给其他函数,但我无法这样做。函数获得偶数个变量参数,然后必须在数组中转换。以下是示例。

过程abc1得到两个参数(k k)而不是abc1过程,这些过程必须传递给proc abc,其中列表要进行数组转换。列表到数组转换在proc1中工作,即abc1,但不在第二个proc中,即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)

3 个答案:

答案 0 :(得分:13)

最佳解决方案:扩展替代

正确的方法是确保外部过程(以堆栈术语)正确地调用内部过程;如果预期有多个参数,那就是应该提供的参数。随着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代码几乎不需要{{1 }};事实证明这一点已经证明是正确的,这对语言的维护者来说甚至是相当令人惊讶的。

答案 1 :(得分:1)

之间存在很大差异
abc k k

abc [array get arg]

在第一种情况下,您传递两个参数,每个参数都是k。在第二种情况下,您传递的是列表 - 在您的示例中,列出了两个k:k k

Nir的answer明知无误地解决了这个问题,但更好的解决方案是编写abc1以便正确调用abc

proc abc1 {args} {
  array set arg $args
  set l2 [array get arg]
  eval abc $l2
  # or just
  # eval abc $args
}

答案 2 :(得分:-1)

当您通过abc $l2时,您将abc1的args作为单个参数传递给abc。因此,在abc args中包含一个包含单个项目的列表({k 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
}