как передать переменные аргументы из одной функции в другую в tcl - PullRequest
4 голосов
/ 30 декабря 2010

Я хочу передать переменные аргументы, полученные в одной функции, другой функции, но я не могу это сделать.Функция получает четное число переменных аргументов и затем должна быть преобразована в массив.Ниже приведен пример.

Процедура abc1 получает два аргумента (k k), а не процедуру abc1, которые должны быть переданы в proc abc, где необходимо выполнить преобразование списка в массив.Преобразование списка в массив работает в proc1, т.е. 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)

Ответы [ 3 ]

13 голосов
/ 31 декабря 2010

Лучшее решение: замена расширения

Правильный подход - обеспечить, чтобы внешняя процедура (в терминах стека) правильно вызывала внутреннюю процедуру; если ожидается несколько аргументов, это то, что должно быть предоставлено. С появлением 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 Command

Если по какой-то причине вы все еще используете старые версии 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 или более ранней, и слишком много развертываний еще предстоит обновить.) Используйте синтаксис расширения, если можете. Действительно, идиоматический код Tcl для 8.5 и более поздних версий вряд ли когда-либо будет нуждаться в eval; степень, в которой это оказалось правдой, даже удивляла тех, кто поддерживал язык.

1 голос
/ 30 декабря 2010

Есть большая разница между

abc k k

и

abc [array get arg]

В первом случае вы передаете два аргумента, каждый из которых равен k. Во втором случае вы передаете список вещей - в вашем примере список из двух k: k k.

Nir's answer сознательно решает эту проблему, но лучшим решением будет написать abc1, чтобы он правильно вызывал abc:

proc abc1 {args} {
  array set arg $args
  set l2 [array get arg]
  eval abc $l2
  # or just
  # eval abc $args
}
0 голосов
/ 30 декабря 2010

Когда вы передаете abc $l2, вы передаете args abc1 в качестве единственного аргумента 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
}
...