13

TCLのupvarコマンドについて質問があります。upvarコマンドを使用すると、他のプロシージャでグローバル変数またはローカル変数への参照があります。私は次のコードを見ました:

proc tamp {name1 name2} {
    upvar $name1 Ronalod
    upvar $name2 Dom 
    set $Dom "Dom"
}

このプロシージャはと呼ばれtamp name1 name2、その外部にグローバル変数name1、name2が定義されていません。この場合、このupvarはどのように機能しますか?

4

4 に答える 4

18

を呼び出すとupvar 1 $foo bar、名前が変数に含まれている呼び出し元のスコープ内の変数が検出されfoo、ローカルbar変数がそのエイリアスになります。変数が存在しない場合、未設定の状態で作成されます(つまり、変数レコードは存在しますが、値はありません。実際、実装はNULLその情報を表すためにaを使用します。そのため、TclにはNULL同等のNULLものがありません。 -存在)が、リンクはまだ作成されています。(ローカルスコープが破棄された場合、またはupvarローカル変数を別の場所に向けるために使用された場合にのみ、破棄されます。)

それでは、コードが実際に何をしているのかを見てみましょう。

proc tamp {name1 name2} {
    upvar $name1 Ronalod
    upvar $name2 Dom 
    set $Dom "Dom"
}

最初の行は、プロシージャと呼ばれるコマンドを作成していることtamp、そのプロシージャには2つの必須の仮引数があり、それらの引数はとと呼ばれることを示していname1ますname2

2行目は、呼び出し元の変数名(つまり1、前の説明のレベルインジケーターはオプションですが、慣用的なコードでは強くお勧めします)を、name1変数(つまり、プロシージャの最初の引数)にバインドしていることを示しています。ローカル変数Ronalod。今後、そのローカル変数へのすべてのアクセス(スタックフレームの有効期間が終了するまで)は、呼び出し元のバインドされた変数に対して実際に実行されます。

name23行目は、 (2番目の引数)とDom(ローカル変数)を除いて、ほとんど同じです。

4行目は実際にはかなりファンキーです。変数を読み取ってDom変数名(つまり、プロシージャ呼び出しの2番目の引数で指定された変数)を取得し、その名前付き変数を値に設定しますDom。Tclでは、変数について話すのではなく、変数$から読み取るために使用することを忘れないでください。

プロシージャ呼び出しの結果は、本体の最後のコマンドの結果になります(つまり、リテラルDomset、変数の内容を結果として、割り当てたばかりの値を生成するためです)。(最後の行は、手順本体の終わりにすぎないため、まったく面白くありません。)

このコマンドを呼び出した最終的な結果は、2番目の引数がまたはのいずれRonalodかを含む変数を指定しない限り、実際にはほとんど何も起こりませんDom。それはかなり紛らわしいです。もちろん、紛らわしいビットはset、変数の最初の引数を持つファンキーです。(これはほとんどの場合悪い考えです。これは悪いコードの臭いの指標です。)代わりにこれを使用した場合、状況はより単純になります。

set Dom "Dom"

この場合、Dom結合されている変数(つまり、プロシージャの2番目の引数によって名前が付けられている変数)はDom;に設定されます。変数は事実上、参照によって渡されます。その余分な$ものは大きな違いを生みます!

于 2012-06-15T13:07:53.323 に答える
3

name1とname2は、呼び出し元のスコープには存在しません。これらは、単にprocのパラメーターです。たとえば、次のようにprocを呼び出すことができます。

% set first "James"
James
% set last "Bond"
Bond
% tamp first last
Dom

現状では、procは実際には何もしません。最後の行を次のように変更すると、より意味があります。

proc tamp {name1 name2} {
    upvar $name1 Ronalod
    upvar $name2 Dom 
    set Dom "Dom"
}
% tamp first last
Dom
% puts $first
James
% puts $last
Dom

upvarとuplevelの使用について私が見た中で最高のガイドの1つは、RobMayoffのガイドhttp://www.dqd.com/~mayoff/notes/tcl/upvar.htmlです。


name1とname2は単なる入力パラメーターであり、グローバルに存在する必要がないことを確認するのに役立つ例をさらに追加します。この例をtclshで実行し、より意味があるかどうかを確認します。

% proc tamp {name1 name2} {
    upvar $name1 Ronalod
    upvar $name2 Dom
    puts "Ronalod=$Ronalod, Dom=$Dom"
    set Ronalod "Brian"
    set Dom "Fenton"
    puts "NOW: Ronalod=$Ronalod, Dom=$Dom"
}
%
% tamp name1 name2
can't read "Ronalod": no such variable
% set first "James"
James
% set last "Bond"
Bond
% tamp first last
Ronalod=James, Dom=Bond
NOW: Ronalod=Brian, Dom=Fenton
% puts $first
Brian
% puts $last
Fenton
于 2012-06-15T10:18:47.783 に答える
1

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また、グローバル変数へのアクセスに関するものではないことにも注意してください。これは通常、 globalandvariableコマンドを使用して実現されます。代わりに、値ではなく変数upvarを操作するために使用されます。 これは通常、「その場で」何かを変更する必要がある場合に必要です。この良い例の1つは、リストを含む変数の名前を受け取り、そのリストに1つ以上の要素を追加して、その場所で変更するコマンドです。これを実現するために、リスト値自体だけでなく、変数の名前を渡します。ここで、これを変数ではなく値を受け入れるコマンドと比較して、リストを取得してのリストを生成します。lappendlappendlinsert

もう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」に変更します。

したがって、意図をより明確にするために、多くの人々は、スタックフレームの数を常に指定することを好みますupvarupvar 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」はグローバルスコープを意味します。そうすれば、グローバル変数にバインドできますが、元の例のプロシージャは、グローバルスコープで実行された場合にのみグローバル変数にバインドされます。

于 2012-06-15T13:19:48.890 に答える
1

tclのupvarは、procの実行後に使用される変数を定義するために使用しています。例:

proc tamp {name1 name2} {
    upvar 1 $name1 Ronalod
    upvar 1 $name2 Dom 
    set $Dom "Dom"
}

#Call the proc tamp

tamp $name1 $name2

#Now we can use the vars Ronalod and Dom

set all_string "${Ronalod}${Dom}"

1次のコマンドの番号

upvar 1 $name2 Dom 

変数を使用できるレベルを示します。たとえば、2

upvar 2 $ name2 Dom

だから私はこれを行うことができます:

proc tamp_two {name1 name2} {
      # do something ...
      tamp $name1 $name2
}

proc tamp {name1 name2} {
    upvar 2 $name1 Ronalod
    upvar 2 $name2 Dom 
    set $Dom "Dom"
}

#Call the proc tamp_two

tamp_two $name1 $name2

#Now we can use the vars Ronalod and Dom

set all_string "${Ronalod}${Dom}"

これは、で可能である可能性があります。upvar 2保持するupvar 1と、機能しません。

これがお役に立てば幸いです。

于 2012-06-15T14:10:28.320 に答える