TCLには関数ポインターの概念がありますか?
-
05-07-2019 - |
質問
TCLを使用して、戦略パターンのようなものを実装したいと思います。 「戦略」を渡したいTCL関数で出力を印刷するため、画面への印刷とログファイルへの印刷を簡単に切り替えることができます。 TCLでこれを行う最良の方法は何ですか?
解決
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を使用する以外に、実際にはコードブロックを代わりに使用できます。これにはいくつかのバリエーションがあります。最初は最も明白で、単に評価
します。
set strategy {
puts $x
}
set x "Hello"
eval $strategy
unset x
これは機能しますが、いくつかの欠点があります。まず、明らかなことですが、両方のコードは、引数に共通の名前を使用するように共謀しなければなりません。これは、1つの名前空間の頭痛(procs)を別の(locals)に置き換えます。これは間違いなく実際には最悪です。
あまり明らかではないが、evalはバイトコードをコンパイルせずに意図的に引数を解釈する。これは、動的に生成される通常は一意の引数でevalが呼び出されると想定されているため、バイトコードを一度だけ使用する場合は、ブロックをすぐに解釈する場合に比べて、バイトコードへのコンパイルは非効率的です。これは簡単に修正できるので、ここにイディオムがあります:
set x "Hello"
if 1 $strategy
unset x
eval
とは異なり、 if
はコードブロックをコンパイルおよびキャッシュします。 $ strategy
ブロックが1つだけ、またはほんの少しの異なる可能な値である場合、これは非常にうまく機能します。
これは、ローカル変数を使用してブロックに引数を渡すという不愉快さをまったく助けません。 substitutions <を実行するなど、多くの方法があります。 / a>と同様に、tkはコマンド引数を%
で置き換えます。 uplevel
または upvar
を使用して、いくつかのハック的なことを試してみることができます。たとえば、これを行うことができます:
set strategy {
puts %x
}
if 1 [string map [list %% % %x Hello] $strategy]
渡される引数があまり変わらないという偶然に、これはバイトコードのコンパイルの観点からうまく機能します。一方、引数が頻繁に変更される場合は、 if 1
の代わりに eval
を使用する必要があります。とにかく、これは引数の点ではあまり良くありません。特別な構文を使用しているため、合格したものとそうでないものについて混乱する可能性は低くなります。また、これは、コードブロックを返す前に変数置換を使用する場合に役立ちます。 set strategy&quot; $ localvar%x&quot;
のように。
幸いなことに、tcl 8.5には真の匿名関数があります。 apply
コマンドを使用します。 applyコマンドの最初の単語は、 proc
への引数が取り除かれたかのように、引数と本文のリストになります。残りの引数は、引数としてすぐに匿名コマンドに渡されます。
set strategy [list {x} {
puts $x
}]
apply $strategy "Hello"
変数関数の使用はどうですか? TCLのことはあまり覚えていません(しばらく経っていますが...)
- [$ var param1 param2]
- [$ var] param1 param2
- $ var param1 param2
間違っている場合は、誰でも自由に修正できます。
% set val 4444
4444
% set pointer val
val
% eval puts $pointer
4444
% puts [ set $pointer ]
4444
% set tmp [ set $pointer ]
4444
ジャクソンの方法が機能する理由を明確にするために、TCLでは everything は文字列であることを思い出してください。リテラル文字列、関数、変数、またはそれが何であろうと、すべては文字列です。 「関数ポインタ」を渡すことができます; &quot; data pointer&quot;と同じように、オブジェクトの名前を先頭に&quot; $&quot;を付けずに使用するだけです。
上記のすべて。ただし、名前空間から名前空間に移動するときは、ブレークを取得しないようにするために、渡す [namespace current] :: proc_name
として使用できます。
OOメソッドの場合、このスレッドの内容に従う必要があります。特定のオブジェクトのメソッドをTclの入力引数として渡します
Godspeed。