質問
私は現在、演習のための私の主な言語としてガイルを使用してSICPて働いています。章3.5の演習を実施している間、私は奇妙な行動を発見しました。私は、さまざまなプラットフォーム上でガイル1.4、ガイル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)
だから、プロシージャ定義に埋め込まれたときに、(... yと定義する)の仕事をしません、REPLでの地球環境での手続きの外、それは罰金を作品に対します。
何が私がここに間違っているのでしょうか?必要であれば、私はあまりにも、補助コード(すなわち、積分、ストリームマップの定義など)を投稿することができます。短所ストリームのためのシステム依存のコードを除いて、それらはすべて本です。次のようにガイルのための短所ストリームの私自身の実装があります:
(define-macro (cons-stream a b)
`(cons ,a (delay ,b)))
解決
あなたがsolve
内でそれらを配置するときに、REPLで1によって定義を1つずつ評価したときに何が起こるかとの主な違いは、最初のケースでは、彼らはこのように表現y
が参照(stream-map <some-function> y)
がすでにあり、順番に評価されていることですスコープは、内部定義またはletrec
有するのに対し、それはまだ利用できません。
Funnily十分、SICPを通過するときに私が使用MIT Schemeは、当時はそのような問題がなかったし、今でも扱い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の環境のこの特異性は、本に影響を与えた...または多分それは他の方法の周りにいます。
他のヒント
あなたはお互いに依存して内部のDEFINEを持つことはできません。言語仕様は、明示的に(R5RS 5.2.2)これを述べます:
...それぞれのの式を評価することが可能でなければなりません。の中のすべての内部定義ののボディの割り当てまたは任意のの変数の値を参照することなくem>の定義されています。
あなたはインタプリタがランダムな順序で身体の前に、すべてのDEFINESを収集し、それらを評価しているのにと考えることができます。順序はランダムですので、あなたが仕事にそれを期待していた場合、任意の相互依存性がないことができます。
それはすべてのスキームでの作業にはないだろうと言い、SOLVE定義(#71)に接続脚注もあります。
あなたは、ネストされたとすることができますように一つの定義は、他の範囲で非常に明確であるようにコードを記述する必要が
(define (solve f y0 dt) (let ((y (integral (delay dy) y0 dt))) (let ((dy (stream-map f y))) y)))
(R5RS 4.2.2からの引用を参照)私は今y
sに「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)
<時間>
同様の修正を加えて、ミハエルの例のコードはすぐに1つの定義の手順ではなく、変数などとして働いています:
(let ()
(define (xs) '(1 2 3))
(define (ys) (map (lambda (x) (+ x 1)) (xs)))
(ys))
ガイル1.8.6で動作します。