TCL を使用しており、 Strategy Patternのようなものを実装したいと考えています。TCL関数で出力を印刷するための「戦略」を渡したいので、画面への印刷とログファイルへの印刷を簡単に切り替えることができます。TCLでこれを行う最良の方法は何ですか?
8 に答える
TCL では、プロシージャの名前を変数に格納し、その変数を使用してプロシージャを呼び出すことができます。それで
proc A { x } {
puts $x
}
set strat A
$strat Hello
プロシージャ 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
これは機能しますが、いくつかの欠点があります。まず明らかなのは、両方のコードが結託して、引数に共通の名前を使用する必要があることです。これにより、1 つの名前空間の問題 (procs) が別の問題 (locals) に置き換わります。
それほど明らかではありませんが、eval はバイトコードをコンパイルせずにその引数を意図的に解釈します。これは、 eval が動的に生成された、通常は一意の引数で呼び出されると想定されているためです。バイトコードが一度しか使用されない場合、ブロックをただちに解釈するのに比べて、バイトコードへのコンパイルは非効率的です。これは簡単に修正できるので、イディオムは次のとおりです。
set x "Hello"
if 1 $strategy
unset x
if
は とは異なりeval
、そのコード ブロックをコンパイルしてキャッシュします。$strategy
ブロックが 1 つまたはほんの一握りの異なる可能な値である場合、これは非常にうまく機能します 。
これは、ローカル変数を使用してブロックに引数を渡すという厄介なことにはまったく役立ちません。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 param1 param2]
- [$var] param1 param2
- $var param1 param2
私が間違っていたら、誰でも自由に訂正してください。
Jackson の方法が機能する理由を明確にするために、TCL ではすべてが文字列であることを思い出してください。リテラル文字列、関数、変数など、どのようなものを扱っていても、すべてが文字列です。「関数ポインタ」は、「データ ポインタ」と同じように渡すことができます。先頭に「$」を付けずにオブジェクトの名前を使用するだけです。