3

与 TCL 合作,我想实现类似Strategy Pattern的东西。我想在 TCL 函数中传递用于打印输出的“策略”,这样我就可以轻松地在打印到屏幕和打印到日志文件之间切换。在 TCL 中执行此操作的最佳方法是什么?

4

8 回答 8

18

TCL 允许您将过程的名称存储在一个变量中,然后使用该变量调用该过程;所以

proc A { x } {
   puts $x
}

set strat A
$strat Hello

将调用 proc A 并打印出 Hello

于 2008-10-31T19:20:12.427 回答
6

除了显示如何将过程分配给变量的答案之外,您还可以将过程的名称作为参数传递给另一个过程。这是一个简单的例子:


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

于 2008-11-08T06:11:54.717 回答
4

上面列出的稍微扩展的示例可以更清楚地说明策略模式:

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
于 2008-11-01T02:54:00.903 回答
3

除了使用 proc,您实际上还可以使用代码块。这有一些变化。首先是最明显的,只是evaling它。

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对命令参数进行替换相同的方式进行替换。%您可以尝试使用 upuplevelupvar. 例如,您可以这样做:

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"
于 2009-07-25T04:27:54.580 回答
1
% set val 4444
4444

% set pointer val
val

% eval puts $$pointer
4444

% puts [ set $pointer ]
4444

% set tmp [ set $pointer ]
4444
于 2009-08-18T08:18:48.803 回答
0

如何使用变量函数?我不记得太多 TCL(已经有一段时间了......)但也许其中一个可以满足您的需要:

  • [$var 参数 1 参数 2]
  • [$var] 参数 1 参数 2
  • $var 参数 1 参数 2

如果我错了,任何人都可以自由纠正我。

于 2008-10-31T13:28:28.720 回答
0

为了澄清杰克逊的方法为什么有效,请记住在 TCL 中,一切都是字符串。无论您使用的是文字字符串、函数、变量还是其他任何东西,一切都是字符串。您可以像传递“数据指针”一样传递“函数指针”:只需使用不带前导“$”的对象名称。

于 2009-03-18T17:23:01.417 回答
0

上述所有内容,尽管在从命名空间移动到命名空间时,您可能希望将其用作传递[namespace current ]::proc_name,以确保不会出现任何中断。
对于 OO 方法,您需要遵循此线程中的内容:在 Tcl Godspeed中将特定对象的方法作为输入参数传递。

于 2018-05-31T11:06:22.993 回答