TclインタープリターがTclで記述されたプロシージャに入ると、コードの実行中にそのプロシージャにローカルな変数の特別なテーブルが作成されます。 このテーブルには、「実際の」ローカル変数と他の変数への特別な「リンク」の両方を含めることができます。set
このようなリンクは、Tclコマンド(など)に関する限り、「実際の」変数と区別できませんunset
。
これらのリンクはupvar
コマンドによって作成され、任意のスタックフレーム(グローバルスコープ-フレーム0を含む)上の任意の変数へのリンクを作成できます。
Tclは非常に動的であるため、その変数はいつでも出入りする可能性があり、によってリンクされた変数は、Tclへupvar
のリンクが作成された時点では存在しない可能性があります。次の点に注意してください。
% unset foo
can't unset "foo": no such variable
% proc test name { upvar 1 $name v; set v bar }
% test foo
bar
% set foo
bar
最初に「foo」という名前の変数が存在しないことを示し、次にそれを使用するプロシージャに設定しupvar
(そして変数は自動作成されます)、次にプロシージャが終了した後に変数が存在することを示します。
upvar
また、グローバル変数へのアクセスに関するものではないことにも注意してください。これは通常、 global
andvariable
コマンドを使用して実現されます。代わりに、値ではなく変数upvar
を操作するために使用されます。 これは通常、「その場で」何かを変更する必要がある場合に必要です。この良い例の1つは、リストを含む変数の名前を受け取り、そのリストに1つ以上の要素を追加して、その場所で変更するコマンドです。これを実現するために、リスト値自体だけでなく、変数の名前を渡します。ここで、これを変数ではなく値を受け入れるコマンドと比較して、リストを取得して別のリストを生成します。lappend
lappend
linsert
もう1つの注意点は、デフォルトでは(2つの引数の形式で)、グローバル変数ではなく、スタックの1つ上のレベルupvar
で指定された名前の変数にリンクすることです。つまり、これを行うことができます:
proc foo {name value} {
upvar $name v
set v $value
}
proc bar {} {
set x ""
foo x test
puts $x ;# will print "test"
}
この例では、プロシージャ「foo」はローカル変数をプロシージャ「bar」に変更します。
したがって、意図をより明確にするために、多くの人々は、スタックフレームの数を常に指定することを好みますupvar
。upvar 1 $varName v
これは、同じupvar $varName v
ですがより明確です。
これのもう1つの便利なアプリケーションは、上昇するスタックレベルをゼロに指定することにより、ローカル変数を参照することです。このトリックは、配列内の変数にさらに便利にアクセスするのに役立つ場合があります。
proc foo {} {
set a(some_long_name) test
upvar 0 a(some_long_name) v
puts $v ;# prints "test"
upvar a(some_even_more_long_name) x
if {![info exists x]} {
set x bar
}
}
upvar
ボーナスとして、「#」プレフィックスを使用して指定されたスタックフレームの絶対数も理解すること に注意してください。「#0」はグローバルスコープを意味します。そうすれば、グローバル変数にバインドできますが、元の例のプロシージャは、グローバルスコープで実行された場合にのみグローバル変数にバインドされます。