与 TCL 合作,我想实现类似Strategy Pattern的东西。我想在 TCL 函数中传递用于打印输出的“策略”,这样我就可以轻松地在打印到屏幕和打印到日志文件之间切换。在 TCL 中执行此操作的最佳方法是什么?
8 回答
TCL 允许您将过程的名称存储在一个变量中,然后使用该变量调用该过程;所以
proc A { x } {
puts $x
}
set strat A
$strat Hello
将调用 proc A 并打印出 Hello
除了显示如何将过程分配给变量的答案之外,您还可以将过程的名称作为参数传递给另一个过程。这是一个简单的例子:
proc foo { a } {
puts "a = $a"
}
proc bar { b } {
puts "b = $b"
}
proc foobar { c } {
$c 1
}
foobar foo
foobar bar
这将打印 a = 1 和 b = 1
上面列出的稍微扩展的示例可以更清楚地说明策略模式:
proc PrintToPDF {document} {
<snip logic>
}
proc PrintToScreen {document} {
<snip logic>
}
proc PrintToPrinter {document} {
<snip logic>
}
set document "my cool formatted document here"
set printMethod "printer"
switch -- $printMethod {
"printer" {
set pMethodName "PrintToPrinter"
}
"pdf" {
set pMethodName "PrintToScreen"
}
"screen" {
set pMethodName "PrintToPDF"
}
}
$pMethodName $document
除了使用 proc,您实际上还可以使用代码块。这有一些变化。首先是最明显的,只是eval
ing它。
set strategy {
puts $x
}
set x "Hello"
eval $strategy
unset x
这可行,但有一些缺点。首先很明显,两段代码必须共同使用参数的通用命名。这用另一种(locals)替换了一个令人头疼的命名空间(procs),这可以说实际上更糟。
不太明显的是 eval 故意在不编译字节码的情况下解释其参数。这是因为假设 eval 将使用动态生成的、通常是唯一的参数调用,并且如果字节码只使用一次,相对于立即解释块,编译为字节码的效率会很低。这更容易解决,所以这是成语:
set x "Hello"
if 1 $strategy
unset x
if
与 不同eval
,它会编译和缓存它的代码块。如果$strategy
块只是一个或只是几个不同的可能值,那么这非常有效。
这对于将参数传递给带有局部变量的块的讨厌没有任何帮助。有很多方法可以解决这个问题,例如以与 tk 用's对命令参数进行替换相同的方式进行替换。%
您可以尝试使用 upuplevel
或upvar
. 例如,您可以这样做:
set strategy {
puts %x
}
if 1 [string map [list %% % %x Hello] $strategy]
如果传递的参数变化不大,这在字节码编译方面效果很好。另一方面,如果参数经常更改,则应使用eval
而不是if 1
. 就论点而言,这无论如何也好不了多少。由于您使用的是特殊语法,因此不太可能混淆已通过和未通过的内容。如果您想在返回代码块之前使用变量替换,这也很有帮助:如set strategy "$localvar %x"
.
幸运的是,tcl 8.5 有真正的匿名函数,使用apply
命令。apply 命令的第一个词将是参数和正文的列表,就好像这些参数proc
已被取出一样。其余参数立即作为参数传递给匿名命令。
set strategy [list {x} {
puts $x
}]
apply $strategy "Hello"
% set val 4444
4444
% set pointer val
val
% eval puts $$pointer
4444
% puts [ set $pointer ]
4444
% set tmp [ set $pointer ]
4444
如何使用变量函数?我不记得太多 TCL(已经有一段时间了......)但也许其中一个可以满足您的需要:
- [$var 参数 1 参数 2]
- [$var] 参数 1 参数 2
- $var 参数 1 参数 2
如果我错了,任何人都可以自由纠正我。
为了澄清杰克逊的方法为什么有效,请记住在 TCL 中,一切都是字符串。无论您使用的是文字字符串、函数、变量还是其他任何东西,一切都是字符串。您可以像传递“数据指针”一样传递“函数指针”:只需使用不带前导“$”的对象名称。
上述所有内容,尽管在从命名空间移动到命名空间时,您可能希望将其用作传递[namespace current ]::proc_name
,以确保不会出现任何中断。
对于 OO 方法,您需要遵循此线程中的内容:在 Tcl Godspeed中将特定对象的方法作为输入参数传递。