在Tcl中,#define的等价物是什么?

3
有没有tcl中类似于C++ #define的命令?我看到过使用proc函数重载来实现“define”的方法,只是想知道是否有更简单直接的方式。

3
你到底想做什么?在动态语言中,编译时的黑客技术价值有限。 - user395760
我有一个重复使用相同参数的函数:foo $a $b $c $d 和 foo $a $b $c $e,因此我想定义 foo_e 和 foo_d 以代替这些函数。 - n00b programmer
4个回答

5
使用interp alias允许您在创建别名时使用ab的内容:
interp alias {} foo_ab {} foo $a $b

如果您需要在调用时使用这些值,则需要使用辅助过程来实现:
proc foo_ab args {
    global a b
    uplevel 1 [list foo $a $b {*}$args]
    # Or this in older Tcl: uplevel 1 [list foo $a $b] $args
}

在8.5中,这也可以用别名和apply来编写:
interp alias {} foo_ab {} apply {args {
    global a b
    uplevel 1 [list foo $a $b {*}$args]
}}

在8.6版本中,您可以通过使用tailcall进一步优化:
interp alias {} foo_ab {} apply {args {
    global a b
    tailcall foo $a $b {*}$args
}}

您还可以使用其他一些更“肮脏”的技巧,例如:

interp alias {} foo_ab {} namespace inscope :: {foo $a $b}

虽然速度不是特别快,但它可以在所有 Tcl 8.* 版本中使用。


如果您的变量ab不是全局范围内的,您可以使用upvar 1 a a; upvar 1 b b替换global a b以在proc/apply中访问它们。 - glenn jackman
很遗憾,对 eval {foo $a $b} 进行别名设置是不起作用的,因为这会多一层展开。 - Donal Fellows

4
Tcl具有一种机制,允许您在解释器中定义过程别名
如果您有
proc foo {one two three} {do something with $one $two $three}

如果你发现你总是将 $a 和 $b 作为前两个参数传递,你可以这样写:
interp alias {} foo_ab {} foo $a $b

现在你可以说:

foo_ab $d   ;# same as "foo $a $b $d"
foo_ab $e   ;# same as "foo $a $b $e"

例子:

proc foo {one two three} {puts [join [list $one $two $three] :]}
set a Hello
set b World
interp alias {} foo_ab {} foo $a $b
foo_ab example  ;# prints "Hello:World:example"
< p > interp alias 命令中的空花括号仅表示当前解释器。您可以使用从属解释器进行许多有趣的操作。


2

或者,您可以定义您的过程来期望d和e作为输入参数,并设置默认值(例如空字符串),例如:

proc foo {a b c {d ""} {e ""} }.....

如果您将有未知数量的输入参数,可以使用单词args,它将创建一个包含args中每个值的列表,例如:
   proc foo {a b c args } {
     foreach bar $args {
       #do whatever...
     }
   }

祝你好运,Brian


目前,默认参数只能出现在参数列表的末尾(除了末尾的 args 参数)。 - Donal Fellows

1
如果你说“接收相同的参数”,是指你反复传递相同的值给 $a$b$c,那么你可以考虑使用全局变量而不是函数参数。在调用函数之前先将值存储在全局变量中,然后你的函数调用简化为 foo $d 等等。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接