Frage

Zur Zeit arbeite ich durch SICP mit Guile als meine primäre Sprache für die Übungen. Ich habe ein seltsames Verhalten während der Durchführung der Übungen in Kapitel 3.5 zu finden. Ich habe dieses Verhalten reproduziert mit Guile 1.4, Guile 1.8.6 und 1.8.7 Guile auf einer Vielzahl von Plattformen und bin sicher, dass es nicht spezifisch für meinen Setup ist.

Dieser Code funktioniert gut (und berechnet e):

  (define y (integral (delay dy) 1 0.001))
  (define dy (stream-map (lambda (x) x) y))
  (stream-ref y 1000)

Der folgende Code sollte Geben Sie ein identisches Ergebnis:

  (define (solve f y0 dt)
    (define y (integral (delay dy) y0 dt))
    (define dy (stream-map f y))
    y)
  (stream-ref (solve (lambda (x) x) 1 0.001) 1000)

Aber es ergibt sich die Fehlermeldung:

standard input:7:14: While evaluating arguments to stream-map in expression (stream-map f y):
standard input:7:14: Unbound variable:
y ABORT: (unbound-variable)

Also, wenn in einer Prozedur-Definition eingebettet, die (definiert y ...) nicht funktioniert, während außerhalb des Verfahrens im globalen Umfeld an der REPL es gut funktioniert.

Was mache ich hier falsch? Ich kann den Hilfscode schreiben (das heißt, die Definitionen von integraler, Strom-Karte etc.), falls erforderlich, auch. Mit Ausnahme des systemabhängigen Code für cons-Strom, sie sind alle in dem Buch. Meine eigene Implementierung von cons-Stream für Guile ist wie folgt:

(define-macro (cons-stream a b)
  `(cons ,a (delay ,b)))
War es hilfreich?

Lösung

Der wesentliche Unterschied zwischen dem, was passiert, wenn man die Definitionen einer nach dem anderen in der REPL bewerten und, wenn Sie sie innerhalb solve platzieren ist, dass im ersten Fall, sie nacheinander ausgewertet werden, damit die y der Ausdruck (stream-map <some-function> y) bezieht bereits in Umfang, während mit internen Definitionen oder letrec, es ist noch nicht verfügbar.

Komischerweise MIT Scheme, die ich verwenden, wenn durch SICP gehen, hatten kein solches Problem damals und noch behandelt letrec und interne definiert anders:

;; this is an error
(letrec ((xs '(1 2 3)) (ys (map (lambda (x) (+ x 1)) xs))) ys)

;; this is still an error (and is treated as such by Guile),
;; yet evaluates to (2 3 4) in MIT Scheme
(let () (define xs '(1 2 3)) (define ys (map (lambda (x) (+ x 1)) xs)) ys)

Ich bin nicht sicher über die ursprünglichen „Arbeiteter Bericht über die Algorithmische Sprache Scheme“ oder R2RS, aber zumindest von R3RS auf internen definiert wurde angeblich letrec gleichwertig. Offenbar diese Besonderheit der Umgebung des MIT beeinflusste das Buch ... oder vielleicht ist es umgekehrt.

Andere Tipps

Sie haben keine internen definiert, die voneinander abhängen; die Sprache spec ausdrücklich erklärt diese (R5RS 5.2.2):

  

... es muss möglich sein, jedes Ausdruck auszuwerten jeder interne Definition in ein Körper ohne Zuordnung oder sich auf den Wert jedes Variable definiert ist.

Sie können als daran denken, obwohl der Dolmetscher alle die DEFINES sammelt und sich vor dem Körper in einer zufälligen Reihenfolge zu bewerten. Da die Reihenfolge zufällig ist, kann es keine Abhängigkeiten, wenn Sie es an die Arbeit erwarten.

Es gibt sogar eine Fußnote zur Definition LöSEN angebracht (# 71), der sagt, dass es nicht auf allen Systemen zur Arbeit zu gehen.

Sie haben den Code zu schreiben, so dass eine Definition sehr klar im Rahmen des anderen ist, wie mit verschachtelten LETs:

(define (solve f y0 dt)
  (let ((y (integral (delay dy) y0 dt)))
    (let ((dy (stream-map f y)))
      y)))

Im Anschluss an die Vorstellung in den Kommentaren (in Bezug auf das Zitat von R5RS 4.2.2) Ich habe jetzt die Definitionen von „y“ und „dy“ in (lambda () ...)s gewickelt:

  (define (solve f y0 dt)
    (define (y) (integral (delay (dy)) y0 dt))
    (define (dy) (stream-map f (y)))
    (y))

Dies stellt sicher, dass die <init> jedes Definitionsteil ohne Bezugnahme auf den Kreis definierten Variablen ausgewertet werden kann, da die Definitionen sind Prozeduren anstatt Ausdrücke mit anderen Variablen wie im ursprünglichen Fall.

Jetzt ist der Code ist sicherlich viel langsamer (da die Funktionen rekursiv erhalten verpackt) und die Stapelgröße erhöht werden muss, aber die folgenden Werke und produzieren das richtige Ergebnis:

  (debug-set! stack 2000000)
  (stream-ref (solve (lambda (x) x) 1 0.001) 1000)

Mit einer ähnlichen Modifizierung Michals Beispielcode funktioniert, sobald man legt die Verfahren eher als Variablen:

  (let ()
    (define (xs) '(1 2 3))
    (define (ys) (map (lambda (x) (+ x 1)) (xs)))
    (ys))

arbeitet auf Guile 1.8.6.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top