Tcl:引号中的 proc 主体而不是大括号

tcl: proc body in quotes instead of curly brackets

如果 proc 的主体用引号而不是大括号提供,是否有任何问题(例如性能损失)?

我的代码在其他过程中生成过程(作为类似 OOP 的方法),例如

proc dataObject {name someData} {
  # more stuff
  proc ${name}.getData {args} \
    "checkArgs $args 0; return $someData"
  # more stuff
}

为了简单起见,我使用引号来启用变量替换。它有效,但我只是担心代码可能没有预编译或其他东西。

感谢您的帮助!

I'm just worried that the code may not be precompiled or something.

这个不用担心,再说Tcl也没有预编译之类的。一旦第一次执行,生成的 proc 的主体将被字节编译(但是主体脚本是 assembled)。

但是,您的过程生成器并不健壮。当 someData 包含使主体脚本或其命令之一不完整的字符串时,引号下的变量替换将破坏您的主体脚本,例如:

dataObject test "do it"

将失败,因为它转换为

return do it;

有几种方法可以以稳健的方式 assemble 脚本(命令序列字符串),一种是使用 list 保护:

proc dataObject {name someData} {
    set procName ${name}.getData
    append body {checkArgs $args 0} \;
    append body [list return $someData] \;
    proc $procName {args} $body
    return [namespace which -command $procName]
}

正如 Donal in another answer 所指出的,嵌套 proc 调用不一定会达到您的预期。但是,在您的情况下,作为生成器,它可能是可以接受的。尽管如此,您可能还是想考虑使用 Tcl lambda 或适当的(好吧,数据)对象?

很难生成完全可靠的代码,但并非不可能,在过程主体周围使用双引号是完全合法的。我建议在执行代码生成之前将插入单词的 space 限制为非空字母数字;这几乎阻止了所有的恶作剧;需要引用其他值(list 命令可以 完全 正确引用你需要的一点鼓励)。通常更容易生成一个别名,将一些额外的参数柯里化到一个不变过程的调用上。这是一个非常简单的例子来说明我的意思:

proc saySomething {a b} {
    puts -nonewline $a
    puts $b
}

proc makeSpeaker {cmd prefix} {
    interp alias {} $cmd {} saySomething "[string trimright $prefix] "
}

makeSpeaker hello "Hello to"
hello Ralf
# ==> Hello to Ralf

如您所见,我们“生成”了包含一个带有 space 的单词的代码,而无需进行复杂的引用。它不能做所有事情,但它可以做很多事情。

并且不要编写自己的伪 OO 代码。这些天不是。从 8.6 开始的 Tcl 带有一个 OO 系统核心,这使得做这些事情更快、更可靠。

oo::class create Speaker {
    variable Prefix
    constructor {prefix} {
        set Prefix "[string trimright $prefix] "
    }
    method say {suffix} {
        puts -nonewline $Prefix
        puts $suffix
    }
}

Speaker create greeting "Hello to"
greeting say Ralf

当然,您可以将这两者混合在一起以获得一些真正强大的方法,但是为了快速理解,示例有点长...