2

私は現在、演習の第一言語としてGuileを使用してSICPを使用しています。3.5章の演習を実装しているときに、奇妙な動作を見つけました。さまざまなプラットフォームでGuile1.4、Guile 1.8.6、Guile 1.8.7を使用してこの動作を再現しましたが、これは私の設定に固有のものではないと確信しています。

このコードは正常に機能します(そしてeを計算します):

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

次のコードでも同じ結果が得られます。

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

ただし、エラーメッセージが表示されます。

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)

したがって、プロシージャ定義に埋め込まれている場合、(define y ...)は機能しませんが、REPLのグローバル環境のプロシージャの外部では正常に機能します。

私はここで何が間違っているのですか?必要に応じて、補助コード(つまり、積分、ストリームマップなどの定義)を投稿することもできます。cons-streamのシステム依存コードを除いて、それらはすべて本の中にあります。Guileのcons-streamの私自身の実装は次のとおりです。

(define-macro (cons-stream a b)
  `(cons ,a (delay ,b)))
4

3 に答える 3

2

相互に依存する内部DEFINEを持つことはできません。言語仕様はこれを明示的に述べています(R5RS 5.2.2):

...定義されている変数の値を割り当てたり参照したりせずに、本文のすべての内部定義の各を評価できる必要があります。

これは、インタプリタがすべてのDEFINEを収集し、本文の前にランダムな順序でそれらを評価しているように考えることができます。順序はランダムであるため、機能することを期待している場合、相互依存関係はありません。

SOLVE定義(#71)には、すべてのスキームで機能するとは限らないという脚注も付いています。

ネストされたLETの場合のように、一方の定義がもう一方の定義のスコープ内に非常に明確になるようにコードを記述する必要があります。

(定義(f y0 dtを解く)
  (let((y(積分(遅延dy)y0 dt)))
    (let((dy(stream-map fy)))
      y)))
于 2010-05-14T10:20:48.050 に答える
1

REPLで定義を1つずつ評価する場合と内部に配置する場合の主な違いはsolve、最初のケースでは、それらが順番に評価されるため、参照するy(stream-map <some-function> y)はすでにスコープ内にあるのに対し、内部定義ではまたはletrec、まだ利用できません。

おかしなことに、SICPを通過するときに使用したMITスキームには、当時はそのような問題はなくletrec、内部定義とは異なる方法で処理されます。

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

元の「アルゴリズム言語スキームに関する改訂レポート」またはR2RSについてはよくわかりませんが、少なくとも内部定義に関するR3RSからは、と同等であると想定されていましたletrec。どうやらMITの環境のこの特異性が本に影響を与えたようです...あるいはその逆かもしれません。

于 2010-05-13T21:08:26.310 に答える
0

コメントのアイデア(R5RS 4.2.2からの引用を参照)に従って、「y」と「」dyの定義を次のようにラップしました(lambda () ...)

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

これにより<init>、定義は元の場合のように他の変数を使用した式ではなくプロシージャであるため、循環定義変数を参照せずに各定義の一部を評価できるようになります。

これで、コードは確かにはるかに遅くなり(関数が再帰的にラップされるため)、スタックサイズを増やす必要がありますが、以下は機能し、正しい結果を生成します。

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

同様の変更を加えると、Michałのサンプルコードは、変数ではなくプロシージャを定義するとすぐに機能します。

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

Guile1.8.6で動作します。

于 2010-05-14T21:32:16.343 に答える