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
当然,您可以将这两者混合在一起以获得一些真正强大的方法,但是为了快速理解,示例有点长...
如果 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
当然,您可以将这两者混合在一起以获得一些真正强大的方法,但是为了快速理解,示例有点长...