2012-03-31 10 views
3

私は次の式に私はちょうど10によって値が増加するたびに名前で上記の手順を抽象化することにより、変更とFOOを呼び出すしかし、私は10スキームの割り当て

(((lambda (x) (lambda() (set! x (+ x 10)) x)) 0)) 

値を取得するたびに評価します!

(define foo ((lambda (x) (lambda() (set! x (+ x 10)) x)) 0)) 

誰がこれを説明していただけますか?

答えて

5

呼び出す関数は、呼び出されるたびに上位10を返すカウンタです。

最初のケースでは、新しい関数を作成してすぐに1回呼び出すと、関数を破棄します。したがって、毎回このカウンタの新しいインスタンスを呼び出すので、10を返す必要があります。

2番目のケースでは、関数を1回作成して変数に割り当て、同じ関数を繰り返し呼び出します。同じ関数を呼び出すので、10,20、...を返します。

2

newacctは正しいですが、これはちょうど私の心がかわいかったので、もっと詳しく説明したいと思います最近。

私は、「環境」と「スコープ」という用語を非常にゆるやかに使用し、本質的に同じことを意味します。そのスキームはlexical scope languageであることを忘れないでください。

スキームが式を評価すると、現在の環境で式の変数の値が検索されます。現在の環境で見つからない場合は、親環境を調べます。値が親環境にない場合は、値を見つけたり、「バインドされていない変数」エラーをスローするトップ(グローバル)レベルに達するまで、次のレベルまで上になります。

いつでもdefineと呼ぶと、シンボルはその環境シンボルテーブルの値に関連付けられます。したがって、最上位レベルでdefineを呼び出すと、エントリがグローバルシンボルテーブルに追加されます。プロシージャの本体でdefineを呼び出すと、そのプロシージャのシンボルテーブルにエントリが追加されます。

defineをプロシージャーに呼び出すことをお勧めします。このプロシージャーのパラメーターbody、および環境からなる項目をシンボル表に作成しています。私は通訳が最初squareが定義されている環境になり(square a)をコールした場合次に

(define a 3) 

(define (square x) 
    (* x x)) 

    GLOBAL 
================= 
    a-|-3 
     | 
square-|-{x} 
     | {(* x x)} 
     | {GLOBAL} ---> All the things defined on the global table 

、それはaが関連付けられていることがわかります。たとえば、手順squareは、このようなエントリ何かを持っているでしょう値3。そして、正方形の体の中のx - > 3そして手続きは9を返します。

プロシージャ内でヘルパープロシージャを定義するときには少し難解ですが、実際の環境でシンボルに関連付けられたものが見つからない場合は、スコープのレベルを上げますそれまでは。また、常に最初の 'マッチ'で停止します。したがって、ローカルxがある場合は、グローバルxよりも優先します(むしろ、グローバルなものを探すことなくローカルxを使用します)。

次に、defineはシンボルテーブルに名前を追加するだけですが、set!は実際にシンボルが関連付けられている値を変更するミューテータです。

したがって(define b "blah")はシンボルテーブルにエントリを入れます。 b => "blah"。何も狂っていない。

(set! b "foo") 
b => "foo" 

しかしset!テーブルに何かを追加することはできません。set!は、実際の値が変更されます。 (set! c "bar") => UNBOUND VARIABLE C

これが最も重要な違いである。その内の他の手順と同様set!行為、それは現在のスコープ内の変数が見つからない場合は、それが一致するものが見つかるまで徐々に、より高いレベルを確認する(またはエラーをスローします)、しかしdefineは常に、それが呼び出されたスコープにバインディングを追加します。

大丈夫ですので、defineset!の違いを理解してください。良い。今質問に。

newacctが指摘しているように、式(((lambda (x) (lambda() (set! x (+ x 10)) x)) 0))は毎回新しいプロシージャを呼び出すため、毎回同じ値を返すことになります。ただし、名前を付けると、プロシージャを呼び出すことによって作成された環境を追跡できます。

(define foo  <--- associated name on the symbol table 
    (lambda (x) <--- scope where x is defined 
     (lambda()   \ 
      (set! x (+ x 10)) |--- body 
      x))    /
     0)  <--- initial value of x 

内側lambdaシンボルxは0の初期値に存在する最初のもので作成された環境内に存在するそれではset!xのシンボルテーブル内のエントリを探し、次の内の1つを発見レベルアップ。エントリを見つけたら、それを変更します。この場合、そこに見つかった値に10を加えます。本当にクールな部分は、グローバルシンボルテーブル内の名前にすべてのものを関連付けるので、各呼び出しの後にその環境が存在し続けることです!これは、データを追跡して操作するためにメッセージパッシングオブジェクトを実装するようなクールなことを行うことができる理由です。

また、この目的のためにlet特殊形式が作成されており、これをより直感的な方法で構成できます。次のようになります。

(define foo  <--- associated name 
    (let ((x 0)) <--- scope where x is defined & initial x value 
     (lambda()   \ 
      (set! x (+ x 10)) |--- body 
      x)))   /