2012-03-31 23 views
3

Wenn ich den folgenden Ausdruck jedes Mal zu bewerten habe ich den Wert 10.Scheme Zuordnung

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

aber ich ändern nur durch das oben beschriebene Verfahren mit einem Namen zu abstrahieren und foo den Wert erhöht sich um 10 jedes Mal rufen !!

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

Kann jemand bitte dies erklären?

Antwort

5

Die Funktion, die Sie anrufen, ist ein Zähler, der bei jedem Aufruf eine Zahl höher als 10 zurückgibt.

Im ersten Fall erstellen Sie jedes Mal eine neue Funktion und rufen sie dann sofort einmal auf und verwerfen dann die Funktion. Jedes Mal rufen Sie eine neue Instanz dieses Zählers zum ersten Mal auf, daher sollte es 10 zurückgeben.

Im zweiten Fall erstellen Sie die Funktion einmal und weisen sie einer Variablen zu und rufen dieselbe Funktion wiederholt auf . Da Sie die gleiche Funktion aufrufen, sollte es 10, 20, ... zurückgeben

2

newacct ist richtig, aber ich würde gerne (viel) mehr Detail gehen, da dies etwas ist, das nur meine Meinung ziemlich blies vor kurzem.

Ich werde die Begriffe 'Umgebung' und 'Scope' ziemlich locker und im Wesentlichen die gleiche Sache bedeuten. Denken Sie daran, dass dieses Schema ein lexical scope language ist.

Wenn das Schema einen Ausdruck auswertet, sucht es in seiner aktuellen Umgebung nach den Werten aller Variablen im Ausdruck. Wenn es in der aktuellen Umgebung keine findet, wird es in der übergeordneten Umgebung angezeigt. Wenn der Wert nicht in der übergeordneten Umgebung ist, sucht er in der nächsten Ebene nach oben usw., bis er die oberste (globale) Ebene erreicht, in der er entweder den Wert findet oder einen Fehler "nicht gebundene Variable" auslöst.

Jedes Mal, wenn Sie aufrufen, verknüpfen Sie ein Symbol mit einem Wert für diese Umgebungssymboltabelle. Wenn Sie also auf der obersten Ebene aufrufen, wird der globalen Symboltabelle ein Eintrag hinzugefügt. Wenn Sie im Hauptteil einer Prozedur aufrufen, wird der Symboltabelle dieser Prozedur ein Eintrag hinzugefügt.

Eine gute Möglichkeit, über Aufruf define auf ein Verfahren zu denken ist, dass Sie einen Eintrag in der Symboltabelle erstellen, die von den Parametern, Körper besteht, und Umgebung dieses Verfahrens. Zum Beispiel würde das Verfahren square einen Eintrag etwas wie diese:

(define a 3) 

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

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

Dann, wenn ich (square a) die Dolmetscher nennen ist zuerst in der Umgebung aussehen würden, in der square definiert ist und es würde feststellen, dass a mit dem zugeordnet ist, Wert 3. Dann x -> 3 innerhalb des Körpers des Quadrats und die Prozedur gibt 9. Cool, macht Sinn.

Die Dinge werden etwas komplizierter, wenn wir damit beginnen, Hilfsverfahren in Prozeduren zu definieren, aber alles, was Sie wirklich beachten müssen, ist, dass wenn es nichts mit einem Symbol in der aktuellen Umgebung zu tun hat, es den Umfang verschiebt bis es geht. Außerdem wird es immer beim ersten "Spiel" aufhören. Also, wenn es eine lokale x ist, wird es über die globale x bevorzugen (eher wird es die lokale x verwenden, ohne jemals eine globale Suche).

Als nächstes erinnern, dass nur Namen der Symboltabelle hinzugefügt, aber set! ist ein Mutator, der tatsächlich die Werte ändert, mit denen ein Symbol verknüpft ist.

Also (define b "blah") setzt einen Eintrag in der Symboltabelle. b => "blah". Nichts verrückt. set! wird der tatsächliche Wert ändern:

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

aber set! kann nichts an den Tisch. (set! c "bar") => UNBOUND VARIABLE C.

Dies ist der wichtigste Unterschied:set! verhält sich wie jedes andere Verfahren, dass, wenn es zunehmend höheren Ebenen nicht die Variable im aktuellen Bereich finden, wird es überprüfen, bis eine Übereinstimmung gefunden wird (oder einen Fehler wirft), aber fügt dem Bereich, in dem es aufgerufen wird, immer eine Bindung hinzu.

In Ordnung, so verstehen Sie den Unterschied zwischen und set!. Gut. Nun zu der Frage.

Der Ausdruck (((lambda (x) (lambda() (set! x (+ x 10)) x)) 0)), wie Newacct darauf hingewiesen hat, wird jedes Mal den gleichen Wert zurückgeben, weil Sie jedes Mal eine neue Prozedur aufrufen. Wenn Sie es jedoch benennen, können Sie die durch den Aufruf der Prozedur erstellte Umgebung verfolgen.

(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 

So ist die inneren lambda existiert im Innern der durch die ersten geschaffen Umgebung, in der das Symbol x bei einem Anfangswert existiert von 0. Dann set! sucht nach einem Eintrag in der Symboltabelle für x und so findet man in den nächsten aufleveln. Sobald es den Eintrag gefunden hat, wird es geändert, in diesem Fall wird 10 zu dem Wert addiert, der dort gefunden wird. Der wirklich coole Teil ist, dass, da Sie das Ganze einem Namen in der globalen Symboltabelle zugeordnet haben, diese Umgebung nach jedem Aufruf bestehen bleibt! Aus diesem Grund können wir coole Dinge wie Message-Passing-Objekte implementieren, um Daten zu verfolgen und zu manipulieren!

Auch die let spezielle Form wurde für diesen Zweck erstellt, und kann eine intuitivere Möglichkeit sein, dies zu strukturieren. Es würde so aussehen:

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