2

这是 Tcl 8.4。

我正在尝试制作一个可以在用户指定的任何其他程序上执行常见操作的程序。

例如:

proc process_menu_item {command args {count 1}} {
    set inf     [expr {$count == -1 ? 1 : 0}]
    set end_str [expr {$count == -1 ? "inf" : $count}]
    set loop_cnt 0

    while {$inf || $count > 0} {
        incr loop_cnt
        puts [format "------- %s -------" [bold "LOOP $loop_cnt of $end_str"]]

        $command $args
    }
    return
}

我希望这个过程只会执行指定的 $command $count 次。我遇到的问题是传递参数。

假设我想调用这样的过程:

proc example {{par_a {-1}} {par_b {-1}} {par_c 1}} {
    puts "All params: $par_a $par_b $par_c"
}

我该怎么打电话process_menu_item?甚至可能吗?我尝试了一个字符串,一个列表,但最终par_a得到了一个包含所有参数的大列表。

谢谢你。

4

3 回答 3

4

(First off, the args parameter to a procedure is only magical when it is last. Put anything after it and it becomes just an ordinary variable. Just so you know. Of course, that might even be what you need here!)

To call out of process_menu_item, you'd want to change:

$command $args

to this (Tcl 8.4 or earlier):

eval $command $args

Well, if you are paranoid you actually write this:

eval [list $command] [lrange $args 0 end]

Or you use Tcl 8.5 (or later):

$command {*}$args

Then, you'd probably build the call to process_menu_item like this:

process_menu_item frobnicate [list foo bar]

There are a few other ways to do it rather than using list (many Tcl commands can produce lists) or you could use a literal:

process_menu_item frobnicate {foo bar}

What isn't recommended is putting anything in that literal refers to variables inside process_menu_item (which would only work with the shortest eval-based form). It lets you do awesome stuff, but it's also incredibly fragile. If you want to pass in the loop variable, that's quite possible but it requires techniques that are a little more advanced (and you should ask another question).

于 2012-04-28T07:08:50.857 回答
2

你总是可以构造一个包装函数:

proc wrapper {arg_list} {
    example [lindex $arg_list 0] [lindex $arg_list 1] [lindex $arg_list 2]
}

TCL 8.5 具有专为此类事情设计的{*}运算符:

% proc test {a b c} {
    puts a
    puts b
    puts c
}
%
% set inp [list "A" "B" "C"]
A B C
% test $inp
wrong # args: should be "test a b c"
% test {*}$inp
a
b
c
于 2012-04-27T23:00:20.943 回答
0

I'm not able to give a more comprehensive answer right now, but interp alias might get you where you want to go. There's an example at rosettacode.org. If that doesn't fit, perhaps explore command traces -- those fire for every command and you can make choices based on the command name.

于 2012-04-28T10:57:39.217 回答