【问题标题】:how to pass variable arguments from one function to other in tcl如何在 tcl 中将变量参数从一个函数传递给另一个函数
【发布时间】:2010-12-30 06:59:45
【问题描述】:

我想将一个函数中获得的变量参数传递给另一个函数,但我不能这样做。函数获取偶数个变量参数,然后必须将其转换为数组。下面是示例。

Procedure 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)

【问题讨论】:

  • 堆栈跟踪中的行号错误;这可能是因为我重新格式化,或者可能是因为在发布之前进行了蒸馏。无论哪种方式,这对问题都不是很重要。

标签: tcl variadic-functions


【解决方案1】:

最佳解决方案:扩展替换

正确的做法是确保外部过程(在堆栈方面)正确调用内部过程;如果需要多个参数,则应提供该参数。随着 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(即 Tcl 8.4 或更早版本),则使用 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 ;这在多大程度上被证明是正确的,甚至让语言的维护者感到相当惊讶。

【讨论】:

  • 另一种可能比这两种方法更可取的方法是通过引用传递参数并使用upvar 来访问它们,而不是{*}eval
【解决方案2】:

区别很大

abc k k

abc [array get arg]

在第一种情况下,您传递了两个参数,每个参数都是k。在第二种情况下,您传递了一个 list 事物 - 在您的示例中,是一个包含两个 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
}

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 这是用户“angle”之前提交的答案的逐字精确副本(随后被删除)。您是从两个不同的帐户回答还是抄袭了别人的回答?
    • 首先,我将“角度”报告为垃圾邮件,因为他的回答是我的回答的逐字精确副本(而不是相反,您可以查看时间。
    • 其次,至于“有更好的方法来解决这个问题”我不确定:“eval”选项在我看来并不安全,扩展替换仅从 8.5 开始。如果您能想到更好的解决方案,您应该发布它。
    猜你喜欢
    • 2023-02-08
    • 2014-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多