如何驱动这个SICP样本函数?

时间:2016-03-30 12:02:35

标签: scheme racket sicp

请告诉我如何正确地从SICP section 3.5.5驾驶stream-withdraw功能。

(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

2 个答案:

答案 0 :(得分:1)

尝试使用此定义:

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

鉴于stream-cons需要不同的评估规则,它不能作为正常程序实现 - 因此,我们使用宏。出于同样的原因,delay原语也是一个宏。

答案 1 :(得分:1)

Streams并不是变异状态的真正替代品。这是推迟评估的一种方式。

一个例子:

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

如果你看看这里的美好事物,你会发现它在每一步都会产生一个新的百万元素列表,最后只使用前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)

在这个版本中,它会在开始第二个元素的平方之前对结果中的第一个元素执行每个步骤,但程序的结构看起来好像先做方形,然后是双等等。

这可能是一种现代的方法,它使用列表但是每个元素的所有步骤都是切换器和生成器。

至于保持状态,你不需要流。你可以制作一个递归程序直到它完成:

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