web-dev-qa-db-ja.com

自由変数と束縛変数とは何ですか?

私は長い間(実際には長すぎる)プログラミングをしてきましたが、「自由変数」と「バインドされた変数」という用語を理解するのに本当に苦労しています。

私がオンラインで見つけた「説明」のほとんどは、ラムダ計算や形式論理、または公理的意味論のようなトピックについて話すことから始まります。 リボルバーに手を伸ばす .

誰かがこれらの2つの用語を、理想的には実装の観点から説明できますか?それらはコンパイルされた言語で存在できますか、そしてそれらはどの低レベルコードに変換できますか?

21
Roddy

free variableは、関数がinvoked、呼び出されるか使用されます。たとえば、mathの項では、zはパラメーターにバインドされていないため、自由変数です。 x制限付き変数です:

f(x) = x * z

プログラミング言語の用語では、自由変数は、変数名を逆方向に検索して実行時に動的に決定されます 関数呼び出しスタック

制限付き変数の評価は、関数呼び出しのコンテキストに依存しません。これは、最も一般的な最新のプログラミング言語の変数型です。ローカル変数、グローバル変数、およびパラメーターは、すべて境界変数です。

無料の変数は、一部の古代プログラミング言語の「 名前で渡す 」の規約に多少似ています。

いくつかの変数を出力する関数fがあるとします。

def f():
    print(X)

これはPythonです。 Xはローカル変数ではありませんが、その値はPythonの規則に従います:関数が定義されているブロックのチェーンを上方向に検索し、最上位モジュールに到達します。

Python=の場合、Xの値は関数宣言コンテキストによって決定されるため、Xは有界変数であると言います。

仮に、Xが自由変数の場合、これは10を出力するはずです。

X = 2

def f():
    print(X)

def g():
    # X is a local variable to g, shadowing the global X
    X = 10
    f()

Pythonでは、両方のX変数がバインドされているため、このコードは2を出力します。 Xのローカルg変数はローカル変数としてバインドされ、fのローカル変数はグローバルXにバインドされます。

実装

自由変数を使用したプログラミング言語の実装では、各関数が呼び出される場所のコンテキストに注意する必要があります。自由変数ごとに、いくつかの reflection を使用して、使用する変数を見つけます。

フリー変数の値は、ランタイムフローとコールスタックによって大きく決定されるため、通常、コンパイル時に決定することはできません。

19
vz0

変数が自由であるかバインドされているかは相対的です。それはあなたが見ているコードの断片に依存します。

このフラグメントでは、xがバインドされています。

function(x) {return x + x;};

そして、ここでxは無料で発生します:

return x + x;

つまり、自由度はコンテキストのプロパティです。 「xは自由変数です」または「xはバインドされた変数です」とは言わず、代わりにあなたが話しているコンテキストを特定します:「xは式[〜#〜] e [〜#〜]で自由です。 "このため、同じ変数xは、話しているコードのフラグメントに応じて、解放することもバインドすることもできます。フラグメントに変数のバインディングサイトが含まれている場合(たとえば、関数の引数にリストされている場合)、フラグメントはバインドされています。バインドされていない場合は、フリーです。

実装の観点から自由/境界の区別が重要なのは、変数置換がどのように機能するかを実装するときです(たとえば、関数に引数を適用するとどうなるか)。評価手順を検討してください。

(function(x) {return x + x;})(3);
=> 3 + 3
=> 6

xは関数の本体で自由であるため、これは正常に機能します。ただし、xが関数の本体にバインドされている場合、評価には注意が必要です。

(function(x) {return (function(x){return x * 2;})(x + x);})(3);
=> (function(x){return x * 2;})(3 + 3); // careful to leave this x alone for now!
=> (function(x){return x * 2;})(6);
=> 6 * 2
=> 12

私たちの実装がバインドされた出現をチェックしなかった場合、3のバインドされたxを置き換え、間違った答えを与えた可能性があります:

(function(x) {return (function(x){return x * 2;})(x + x);})(3);
=> (function(x){return 3 * 2;})(3 + 3); // Bad! We substituted for a bound x!
=> (function(x){return 3 * 2;})(6);
=> 3 * 2
=> 6

また、フリーvsバウンドはsyntax(つまりコード自体)のプロパティであり、実行時のコードの評価方法のプロパティではないことを明確にする必要があります。 vz0は、動的変数の変数について話します。動的スコープ変数は、自由変数と多少関係がありますが、同義ではありません。 vz0が正しく説明しているように、動的変数スコープは、実行時のコールスタックを調べて同じ名前を共有する変数の値を見つけることにより、自由変数を含む式を評価できる言語機能です。ただし、動的スコープを許可しない言語での変数の自由発生について話すことは依然として理にかなっています。しようとすると、エラー(「x is not defined」など)が発生するだけです。これらの言語でそのような表現を評価する。

そして、私は自分自身を抑えることができません。いつか人々がラムダ計算について言及するとき、あなたがあなたの拳銃を片付ける力を見つけられることを望みます!ラムダ計算は、変数と置換、およびそれ以外をサポートする非常に最小限のプログラミング言語であるため、変数とバインディングについて考えるのに適したツールです。実世界のプログラミング言語には、本質を覆い隠す他の多くのジャンク(動的スコープなど)が含まれています。

10
takeoutweight