2016-03-30 10 views
0

SICP stream-withdraw機能を正しく動作させる方法を教えてください。このSICPサンプル関数をどのように駆動するのですか?

(define (stream-cons a b) (cons a (delay b))) 
(define (stream-car s) (car s)) 
(define (stream-cdr s) (force (cdr s))) 

(define (stream-withdraw balance amount-stream) 
    (stream-cons 
    balance 
    (stream-withdraw (- balance (stream-car amount-stream)) (stream-cdr amount-stream)))) 

; Calling stream-withdraw in the following manner raises an exception. 
(stream-withdraw 100 (stream-cons 0 0)) 
; car: contract violation 
; expected: pair? 
; given: 0 

私はamount-streamを別の方法で構築する必要があると仮定します。

+0

私はSICPでstream-consが特別な形式でなければならないことを発見しました。私の質問を修正してみましょう:金額ストリームを構築する適切な方法は何ですか?また、ユーザーが新しい引き出しを入力している間、機能的な方法でストリームを引き出すためにこのストリームが使用される典型的な方法を見たいと思います。私はまだ地方の州を避けるためにストリームを使う上での重要な要素を見ない。 –

答えて

1

は、この定義を使用してみてください:stream-consは異なる評価規則が必要であることを考えると

(define-syntax stream-cons 
    (syntax-rules() 
    ((stream-cons head tail) 
    (cons head (delay tail))))) 

、それは通常の手順として実装することができない - それゆえ、我々はマクロを使用しています。同じ理由から、delayプリミティブもマクロです。

+0

私の元の質問に答えるお返事ありがとうございます。一人の人のバランスを管理するためにストリームを単純な読み込みループに適用する方法を、OOプログラマのために説明してもいいでしょうか?私は関数型プログラミングがどのように割り当てなしで問題を解決するのか分かりません。 –

+0

おそらく残高の値のストリームを作成することによって?私はSICPのこの章で少し埃が多いですが、IIRCはこれが本の中で説明されています –

+0

ストリームに関する章がこの問題を理解するのに役立つと私の希望でした。残念ながら、バランスストリームの適用は示されていません。あなたが「天秤のための価値の流れ」と言うとき、それは引き金を集める一つの流れがあるように私に聞こえますが、それは変更可能でなければならないのですか?私は本当に混乱しています。 –

1

ストリームは本当に変異状態の置換ではありません。それは評価を遅らせる方法です。

例:

(define one-to-million 
    (let loop ((n #e1e6) (acc '())) 
    (if (zero? n) 
     acc 
     (loop (- n 1) (cons n acc))))) 

(define (list-square lst) 
    (map (lambda (x) (* x x)) lst)) 

(define (list-double lst) 
    (map (lambda (x) (+ x x)) lst)) 

(define (list-sqrt lst) 
    (map sqrt lst)) 

(take (list-sqrt (list-double (list-square one-to-million))) 5) 
; ==> (1.4142135623730951 2.8284271247461903 
;  4.242640687119285 5.656854249492381 7.0710678118654755) 

あなたがここにhappnes何を見れば、あなたはそれが道の各ステップで百万個の要素の新しいリストを作成し、最終的にはちょうど最初に使用していることがわかります5結果ストリームのバージョン:このバージョンで

(define (stream-take s n) 
    (if (zero? n) 
     '() 
     (cons (stream-car s) 
      (stream-take (stream-cdr s) (- n 1))))) 

(define (integers-starting-from n) 
    (stream-cons n (integers-starting-from (+ n 1)))) 

(define from1 (integers-starting-from 1)) ; infinite stream! 

(define (stream-map proc stream) 
    (stream-cons (proc (stream-car stream)) 
       (stream-map proc (stream-cdr stream)))) 

(define (stream-square stream) 
    (stream-map (lambda (x) (* x x)) stream)) 

(define (stream-double stream) 
    (stream-map (lambda (x) (+ x x)) stream)) 

(define (stream-sqrt stream) 
    (stream-map sqrt stream)) 

(stream-take (stream-sqrt (stream-double (stream-square from1))) 5) 
; ==> (1.4142135623730951 2.8284271247461903 
;  4.242640687119285 5.656854249492381 7.0710678118654755) 

第二の要素の二乗を開始する前に、それは結果の最初の要素のための各ステップを行うだろうが、あなたは最初の正方形を行うかのようにプログラムfoの構造が見え、ダブルなど。

おそらく現代のアプローチはリストを使用していますが、各要素のためのすべてのステップは熱心にトランスデューサとジェネレータです。

状態を維持するために、ストリームを必要としません。今、私たちはこれを完全に機能の実装を与えることができます

(let loop ((state '()) (input input)) 
    (if (input-empty? input) 
     state ; finished 
     (loop (process-input (input-get input) state) 
      (input-next input)))) 

(define input '(1 2 3 4)) 
(define input-get car) 
(define input-next cdr) 
(define input-empty? null?) 
(define process-input cons) 

それとも、我々はそれ汚い副作用バージョン作ることができます:

(define input (current-input-port)) 
(define input-get read-line) 
(define input-next values) ; just return the port 
(define input-empty? (lambda (x) (eq? (peek-byte x) eof))) ; check if there is more to read 
(define process-input cons) 
あなたはそれが終了されるまで再帰的手続きを行うことができます
+0

@AttilaKarolyあなたは例を尋ねました。私の答えの最後に私は1つあります。この例でストリームを使用してストリームを使用するのではなく、ユーザー入力の要求や出力の表示が副作用であることを知っているので、純粋性が失われることはわかります。 – Sylwester

関連する問題