Dynamic 如何在运行时动态修改程序体?

Dynamic 如何在运行时动态修改程序体?,dynamic,tcl,Dynamic,Tcl,原则上我想这样做: #grab some value from outer source (i.e. file or list defined by another programer) set original_proc_name foo #define function with a specific name using the string from that outer source proc "helper_[set original_proc_name]" {val} {

原则上我想这样做:

#grab some value from outer source (i.e. file or list defined by another programer)
set original_proc_name foo

#define function with a specific name using the string from that outer source
proc "helper_[set original_proc_name]" {val} {
    #I need to use that string inside the body of the new proc
    return [[set original_proc_name] $val]
}
set lst {dog strong bong}
foreach iter $lst {proc "super_[set iter]" {} "puts \"my name is $iter\" \nputs \"Yay $iter is great!\""}
super_dog
我基本上需要像控制一个
[list…]

有没有办法在运行时动态定义proc主体

我希望这样做,因为我需要定义一组用于测试的过程,这些过程基本上只需调用其他程序员编写的一组具有特定参数的过程

为什么我要定义新的过程?因为这正是我们设置所期望看到的回归(要运行的进程的名称)。

来自Glenn Jackman的特定示例(回答问题):

proc helper\u$original\u proc\u name{val}“return\[$original\u proc\u name\$val]”

我用双引号成功地做到了这一点:

set some_val 7
set new_proc_body "puts $some_val \nputs \[expr $some_val * 2\]"
proc bar {} $new_proc_body
bar
下面是另一个示例来演示该原理:

#grab some value from outer source (i.e. file or list defined by another programer)
set original_proc_name foo

#define function with a specific name using the string from that outer source
proc "helper_[set original_proc_name]" {val} {
    #I need to use that string inside the body of the new proc
    return [[set original_proc_name] $val]
}
set lst {dog strong bong}
foreach iter $lst {proc "super_[set iter]" {} "puts \"my name is $iter\" \nputs \"Yay $iter is great!\""}
super_dog

你已经回答了自己的问题,想必你已经满意了。如果您打算进一步探索编写Tcl的Tcl,那么仍然有一些事情可以考虑。一件事是要记住,名字很少是特别的,例如,没有必要害怕名字
列表

set list {dog strong bong}
正如有时指出的,调用

set set set
是完全有效的Tcl,不会破坏任何东西

另一件事是,
proc
的主体是一个列表(以及一个字符串、一个字符串列表和一个列表),可以这样处理。这是可行的,并且可以避免很多反斜杠:

foreach iter $list {
    proc super_$iter {} [join [list \
        [list puts "my name is $iter"] \
        [list puts "Yay $iter is great!"]] \n]
}
另一种方式:

foreach iter $list {
    lappend body \
        [list puts "my name is $iter"] \
        [list puts "Yay $iter is great!"]
    proc super_$iter {} [join $body \n]
    set body {}
}
还有一个:

set outputs {{my name is $iter} {Yay $iter is great!}}
foreach iter $list {
    set body [lmap output $outputs { list puts [subst $output] }]
    proc super_$iter {} [join $body \n]
}

在Tcl中,代码就是数据,数据也可以是代码。任何你可以用字符串或列表做的事情,你都可以用proc body做。

在Tcl中,没有什么是特别神圣的。你可以将语言中的任何东西与其他任何东西结合使用。特别是,您可以使用任何字符串生成命令(实际上所有命令都会生成空字符串,尽管有些命令只生成空字符串)来生成过程的主体
proc
只是一个普通的Tcl命令(它碰巧生成了其他Tcl命令,尽管还有其他命令也这样做)

不过,在您的具体情况下,使用不同的方法可能会更好

set original_proc_name foo

interp alias {} helper_$original_proc_name {} apply {{cmd val} {
    $cmd $val
}} $original_proc_name
好吧,这看起来有点不可思议。不是真的。让我们从里到外工作

apply
是一个类似于无名过程的命令。它的第一个参数是一个列表(通常有两个元素),它定义了“类似过程”的东西和主体的参数。它的后续参数是实际参数。因此,

apply {{a b} {
    puts "a=$a and b=$b"
}} 123 456
将打印
a=123和b=456
。我们将其用于一个特定的主体,该主体接受两个参数,并将一个参数应用于另一个参数;第一个参数是命令名(可能是),第二个参数是发送给该命令的值。所以我们开始:

apply {{cmd val} {
    $cmd $val
}} puts "Hi"
我们得到了一段非常平凡的代码。好处在哪里

我们在这里使用的是
interp别名。此特定表格:

interp alias {} foo {} bar boo
这样,当您将
foo
用作命令时,Tcl将调用传递给
bar boo
,并将任何额外参数附加到
foo
。在一个简单的例子中:

interp alias {} writeError {} puts stderr
给我们一个命令,将消息写入stderr,如下所示:

writeError "GRUMPY CAT WANTS NO PART OF THIS!"
好的,我们把这两件事放在一起

interp alias {} helper_$original_proc_name {} apply {{cmd val} {
    $cmd $val
}} $original_proc_name
这里,我们正在生成的命令别名是通过字符串替换生成的,我们正在对内部简单调用程序进行部分应用,以便将其锁定为原始命令名。结果是,我们使
helper\u foo
成为一个只接受一个参数的命令,并将该参数传递给原始
foo
进行处理

而且都没有复杂的字符串替换

这种情况可能有点过分,但对于更复杂的任务,它比生成正确的脚本要简单得多。它也比我在自己的代码中使用的略为夸张:

interp alias {} helper_$original_proc_name {} $original_proc_name
但这有着微妙的不同语义



在Tcl 8.6中,您还可以使用
tailcall
。这可以让事情变得更“有趣”。强大的力量带来了巨大的不负责任的机会…

我的方法是简单地检索现有的proc主体,修改脚本并重新定义proc

proc myProc {} {
    puts ABC
}
输出 ABC

输出 基础知识
DEF

不确定您真正想要做什么。这当然是可能的,但请尝试解释一下你真正想用它实现什么。请不要在注释中添加代码。缩进丢失并变得混乱。请把它放在你的问题里。翻译问题中的代码:
proc-helper\u$original\u-proc\u-name{val}“return\[$original\u-proc\u-name\$val]”
为了提高可读性,您可以像使用大括号一样使用带双引号的多行字符串——不需要嵌入
\n
,使用实际的换行符。谢谢!我现在就来试试。我告诉我所有新加入tcl的同事们。他们有时会发现,你可以动态创建的任何字符串都是完全合法的[变量名/过程名/代码段]令人费解;但我不知道可以用proc名称命名变量+1.