Lisp (私はまだ SBCL で Lisp を学んでいます) では、ローカル変数は で宣言されlet
、スコープはその式内のみです。何故ですか?C/C++/Java などの他の命令型言語とは異なり、関数スコープ内のどこでもローカル変数を自由に使用できます。
3 に答える
let とは何かについてのちょっとした洞察です。これは基本的に、「逆向きに綴られた」無名関数のアプリケーションです。
JavaScript はより C に似た言語であり、概念を非常によく示しているため、説明に JavaScript を使用します。
(function(variableA, variableB){
console.log("variableA = " + variableA);
console.log("variableA * variableB = " + variableA * variableB);})(6, 7);
それでは、パーツに名前を付けましょう。from function
to;}
は関数定義です。(definition)(arguments)
アプリケーションです。Let 式は基本的に同じことを行います。つまり、無名関数を引数付きで呼び出し、その関数内で変数として使用します。したがって、前の例を考えると、それを let 形式で書き直すと、次のようになります。
(let(variableA = 6, variableB = 7){
console.log("variableA = " + variableA);
console.log("variableA * variableB = " + variableA * variableB);});
(JavaScript はまだ let をサポートしていないため、上記は実際のコード例ではありませんが、例として示す必要があります)
また、それほど単純ではないことにも注意してください。より複雑なケースでは、別の引数を作成するときに引数の 1 つを参照したい場合があるため、 を(let* ...)
使用するか、この式の引数として関数を使用したい場合に or を使用し(flet ...)
ます(labels ...)
。しかし、一般的な考え方は同じです。
Lisp には、ローカル変数を導入するための構造があります。スコープを設定します。フォームが許可されている場所ならどこでも使用できます。そのための 2 つの主な構造は、LET と LET* です。Ait を使用すると、関数とは別にローカル変数を定義できます。ローカル変数が必要な場合は関数を導入する必要はなく、ローカル変数を最小限のスコープにするために使用します。
また、DEFUN を使用するとローカル変数を宣言できることにも注意してください。これにより、パラメーター リスト内の &AUX でそれらを導入できます。
とあなたが言及している他のスタイルの違いはlet
、純粋に構文糖衣の 1 つです。「どこでも」新しい変数を導入できる言語では、ネストされたスコープがまだありますが、表面の構文でフラット化されるだけです。例えば
{
x = 3;
x++;
y = 4;
print "hi";
z = 42;
}
ここにはスコープが 1 つだけではなく、さまざまなスコープがあります。x
で始まるスコープがありますx = 3
。y
次に、で始まるスコープがありますy = 4
。y
そのスコープの前を参照すると、エラーが発生します。
この種のことは、コードを処理するコードの開発に多くの労力を追加するため、Lisp から除外されています。そのような言語のコンパイラは、そのコードを分析し、ネストされたlet
構造を示す抽象構文ツリー構造を復元する必要があります。これにより、コンパイラの残りのパスは、その情報を再発見するためにコードを再分析し続ける必要がなくなります。
Lisp では、その抽象構文ツリー構造に直接書き込みます。これにより、言語が統一されます。すべてがフォームであり、それが複合フォームの場合は、左端の位置に演算子があり、そのフォームの残りの部分の意味を文脈に依存しない方法で決定します。
たとえば、この最近の質問を参照してください: find free variables in lambda expression . ラムダ式の自由変数を見つけることは、 などの特殊な形式をバインドするlet
か、そのような特殊な形式の小さなレパートリーに展開するマクロによって開始される場合、はるかに簡単になります。
また、関数型スタイルは、とにかく Lisp によって強制されるわけではありませんが、これらの種類の言語よりも一般的であることに注意してください (架空の「{} 言語」の変数定義の中に命令文があることに注意してください)。命令文がない場合、実際にこれに遭遇することはありません。
機能コードでは、次の形式で連続バインディングを使用できlet*
ます。
(let* ((x 1) x = 1;
(y (1+ x)) y = x + 1;
(z (/ x y)) z = x / y;
(sqrt z)) return sqrt(z);
の後の初期化はlet*
、以前のバインディングを参照できます。