動的スコープを使用すると、呼び出し先は呼び出し元の変数にアクセスできます。疑似Cコード:
void foo()
{
print(x);
}
void bar()
{
int x = 42;
foo();
}
動的スコープをサポートする言語でプログラミングしたことがないので、動的スコープの実際のユースケースはどうなるのだろうと思います。
動的スコープを使用すると、呼び出し先は呼び出し元の変数にアクセスできます。疑似Cコード:
void foo()
{
print(x);
}
void bar()
{
int x = 42;
foo();
}
動的スコープをサポートする言語でプログラミングしたことがないので、動的スコープの実際のユースケースはどうなるのだろうと思います。
回答:
動的スコープの非常に便利なアプリケーションは、コールスタック内のすべての関数に新しいパラメーターを明示的に追加する必要なく、コンテキストパラメーターを渡すためのものです。
たとえば、Clojureはbindingによる動的スコープをサポートしています。これを使用して、一時的*out*
に印刷用の値を再割り当てできます。再バインドする*out*
と、バインディングの動的スコープ内でprintを呼び出すたびに、新しい出力ストリームに出力されます。たとえば、すべての印刷出力をある種のデバッグログにリダイレクトする場合に非常に役立ちます。
例:以下のコードでは、do-stuff関数は標準出力ではなくデバッグ出力に出力しますが、これを有効にするために出力パラメーターをdo-stuffに追加する必要がないことに注意してください。
(defn do-stuff []
(do-other-stuff)
(print "stuff done!"))
(binding [*out* my-debug-output-writer]
(do-stuff))
Clojureのバインディングもスレッドローカルであるため、この機能を同時に使用しても問題はありません。これにより、同じ目的でグローバル変数を(ab)使用するよりもバインディングがかなり安全になります。
(免責事項:私は動的スコープ言語でプログラミングしたことがありません)
スコーピングは実装がはるかに簡単で、潜在的に高速です。動的スコープでは、1つのシンボルテーブルのみが必要です(現在利用可能な変数)。このシンボルテーブルからすべてを読み取ります。
Pythonで同じ関数を想像してみてください。
def bar():
x = 42;
foo(42)
def foo(x):
print x
barを呼び出すとき、xをシンボルテーブルに入れます。fooを呼び出すと、barに現在使用されているシンボルテーブルを取得してスタックにプッシュします。次に、xが渡されたfooを呼び出します(関数の呼び出し時に新しいシンボルテーブルに配置されている可能性があります)。関数を終了した後、新しいスコープを破棄して古いスコープを復元する必要があります。
動的スコープでは、これは必要ありません。シンボルテーブルに対して何も実行する必要がないため、関数が終了したときに戻る必要がある命令を知るだけで済みます。
ほとんどの言語での例外処理は動的スコープを利用しています。例外が発生すると、制御は(動的)アクティベーションスタック上の最も近いハンドラに戻されます。
return
、ほとんどの言語のステートメントは、スタック上の呼び出し元に制御を返すため、動的スコープを使用しているとも言えますか?
これが完全に一致するかどうかは100%わかりませんが、少なくとも一般的な意味では、スコープルールの違反または変更に使用できる場所を示すのに十分近づいていると思います。
Ruby言語にはテンプレートクラスERBが付属しています。これは、たとえばRailsでHTMLファイルを生成するために使用されます。使用すると、次のようになります。
require 'erb'
x = 42
template = ERB.new <<-EOF
The value of x is: <%= x %>
EOF
puts template.result(binding)
binding
それはそれらにアクセスして、テンプレートを埋めるためにそれらを使用することができますので手は、ERBのメソッド呼び出しにローカル変数にアクセスできます。(EOF間のコードは文字列であり、<%=%>の間の部分はERBによってRubyコードとして評価され、関数のように独自のスコープを宣言します)
Railsの例はこれをさらによく示しています。記事のコントローラーでは、次のようなものが見つかります。
def index
@articles = Article.all
respond_to do |format|
format.html
format.xml { render :xml => @posts }
end
end
index.html.erbファイルは、次の@articles
ようなローカル変数を使用できます(この場合、ERBオブジェクトの作成とバインディングはRailsフレームワークによって処理されるため、ここには表示されません)。
<ul>
<% @articles.each do |article| %>
<li><%= article.name</li>
<% end %>
</ul>
したがって、バインディング変数を使用することにより、Rubyは異なるコンテキストで1つの同じテンプレートコードを実行できます。
ERBクラスは使用例の1つにすぎません。Rubyでは一般に、Kernel#bindingを使用して、変数とメソッドのバインディングで実際の実行状態を取得できます。これは、異なるコンテキストでメソッドを評価したい場合や、後で使用するためにコンテキストを保持したい場合に非常に役立ちます。
動的スコープの使用例は、グローバル変数の場合と同じIHMOです。動的スコープにより、変数のより制御された更新を可能にするグローバル変数の問題の一部が回避されます。
私が考えることができるいくつかのユースケース:
もちろん、動的スコープは「絶対に必要」ではありませんが、呼び出しチェーンに沿ってトランプデータを渡さなければならない、またはスマートグローバルプロキシとコンテキストマネージャを実装する必要があるという負担から解放されます。
しかし、繰り返しになりますが、動的スコープは、しばしばグローバルとして扱われる種類の要素(ログ、出力、リソースの割り当て/管理など)を扱うときに便利です。
ほとんどありません。たとえば、同じ変数を2回使用しないでください。
void foo() {
print_int(x);
}
void bar() {
print_string(x);
}
では、同じ関数からfooとbarの両方をどのように呼び出すのでしょうか。
これは、単にグローバル変数を使用することと実質的に同じであり、同じ理由で悪いことです。
x = 42; foo(); x = '42'; bar();
?