Question

Je travaille actuellement à travers SICP en utilisant Guile comme langue principale pour les exercices. J'ai trouvé un comportement étrange en mettant en œuvre les exercices au chapitre 3.5. J'ai reproduit ce comportement à l'aide d'une variété de plates-formes Guile 1.4, 1.8.6 et Guile Guile 1.8.7 et je suis certain que ce n'est pas spécifique à ma configuration.

Ce code fonctionne bien (et calcule e):

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

Le code suivant devraient donner un résultat identique:

  (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)

Mais il donne le message d'erreur:

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)

Une fois incorporé dans une définition de procédure, le (y définir ...) ne fonctionne pas, alors qu'à l'extérieur de la procédure dans l'environnement mondial au REPL il fonctionne très bien.

Qu'est-ce que je fais mal ici? Je peux afficher le code auxiliaire (à savoir, les définitions de l'intégrale, le courant-carte, etc.), si nécessaire, aussi. A l'exception du code dépendant du système pour flux de contre, ils sont tous dans le livre. Ma propre mise en œuvre de contre-courant pour Guile est la suivante:

(define-macro (cons-stream a b)
  `(cons ,a (delay ,b)))
Était-ce utile?

La solution

La principale différence entre ce qui se passe lorsque vous évaluez les définitions un par un au REPL et quand vous les placez à l'intérieur solve est que dans le premier cas, ils sont évalués de manière séquentielle, ainsi le y l'expression (stream-map <some-function> y) fait référence est déjà en champ d'application, alors que les définitions internes ou letrec, il est pas encore disponible.

Bizarrement, MIT Scheme, que j'ai utilisé lors de la traversée SICP, avait pas de problème à l'époque et encore traite letrec et définit internes différemment:

;; 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)

Je ne suis pas sûr de l'original « Rapport révisé sur Algorithmique langage Scheme » ou R2RS, mais au moins de R3RS sur internes étaient censés définit comme équivalent à letrec. Apparemment, cette particularité de l'environnement du MIT a influencé le livre ... ou peut-être est l'inverse.

Autres conseils

Vous ne pouvez pas avoir DEFINE internes qui dépendent les uns des autres; la spécification de langage indique explicitement (r5rs 5.2.2):

  

... il doit être possible d'évaluer chaque expression de chaque définition interne dans un body sans affectation ou se référant à la valeur de tout variable étant défini.

Vous pouvez penser à cela comme si l'interprète recueille toutes les DEFINES et les évaluer avant le corps dans un ordre aléatoire. Parce que l'ordre est aléatoire, il ne peut pas être si vous vous attendez interdépendances au travail.

Il y a même une note relative à la définition RÉSOUDRE (# 71) qui dit qu'il ne va pas au travail sur tous les systèmes.

Vous devez écrire le code de sorte que l'une définition est très clairement dans le cadre de l'autre, comme avec imbriqué LETS:

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

Suite à l'idée dans les commentaires (se référant à la citation de r5rs 4.2.2) J'ai maintenant enveloppé les définitions de « y » et « dy » dans (lambda () ...)s:

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

Ceci fait en sorte que que la partie <init> de chaque définition peut être évaluée sans se référer aux variables définies circulaires étant donné que les définitions sont des procédures plutôt que des expressions avec d'autres variables comme dans le cas d'origine.

Maintenant, le code est certainement beaucoup plus lent (puisque les fonctions sera enveloppé récursive) et les besoins de la taille de la pile d'augmenter, mais les travaux suivants et produit le résultat correct:

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

Avec une modification similaire, le code exemple de Michał fonctionne dès que l'on définit les procédures plutôt que des variables:

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

fonctionne sur Guile 1.8.6.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top