Racket, SICP stream learning

在 windows 上重新安装了最新的 Racket 5.2.1.

恍然发现,Common-Lisp 的安装真的比较坑爹啊,Racket 可能才是研究和学习 lisp 比较理想的选择!

不管是 Windows 还是 Ubuntu,自学习 Lisp 以来,Common Lisp 的各种实现 + 开发环境也安装了很多了,每一个配置起来都比较麻烦,也都有这样那样的问题。而相比而言,安装 Racket 却超级傻瓜化。

一番周折后,终于调通了 SICP 220 页开始的对于 stream 的一些示例代码。其中需要注意的是:

书中提到的 cons-stream 是一个 special form, 但是我一开始写成了简单的 function 如下:

; this won't work as a simple function
(define (cons-stream a b)
    (cons a (delay b)))

但这个实际是是不 work 的。如果流比较大或者是无限流,几乎一定会导致无限递归、内存溢出。解决的办法参考了 stack overflow 上一个答案,利用 Racket 的 define-syntax 语法定义宏。其实和 Common Lisp 里宏有点接近,只是定义时使用的语法有差异,用到方括号,详见代码。

和 Common Lisp 中列表的 subseq 函数相似,我简单的实现了一个 stream-subseq 函数,用于截取流中某一区间的子序列,配合 display-stream 函数打印会比较方便的了解流中间任意一段的信息,后面的测试输出代码我基本都这么写了。

通过无限流移位后相加、相乘甚至配合其他运算进行任意组合,是流使用起来感觉最妙的地方。代码中包含了 SICP 原书中附带的一个筛法求素数序列的实现。

通过几个例子简单的试验下来发现,的确 stream 的功效是强大的。因为本质上每一步计算都是惰性求值,所以即使是要估算序列中很后面的数值,也不会像列表那样带来很多分配空间的开销。斐波那契数列可以轻而易举的算到很后面。下面是我调试通过的测试代码:

#lang racket

;(define (delay exp)
;  (lambda () exp))
;  ;(memo-proc (lambda ()
;  ; exp)))
;
;(define (force delayed-object)
;  (delayed-object))
;
;(define (memo-proc proc)
;  (let ((already-run? false) (result false))
;    (lambda ()
;      (if (not already-run?)
;          (begin (set! result (proc))
;                 (set! already-run? true)
;                 result)
;          result))))

(define (stream-car stream) (car stream))

(define (stream-cdr stream) (force (cdr stream)))

; this won't work as a simple function
;(define (cons-stream a b)
;  (cons a (delay b)))

; This is scheme syntax for macro
; http://stackoverflow.com/questions/5610480/scheme-sicp-r5rs-why-is-delay-not-a-special-form
(define-syntax cons-stream
  (syntax-rules ()
    [(cons-stream x y) (cons x (delay y))]))

(define the-empty-stream '())

(define (stream-null? stream)
  (null? stream))

(define (stream-filter pred stream)
  (cond ((stream-null? stream) the-empty-stream)
        ((pred (stream-car stream))
         (cons-stream (stream-car stream)
                      (stream-filter pred (stream-cdr stream))))
        (else (stream-filter pred (stream-cdr stream)))))

(define (stream-ref s n)
  (if (stream-null? s) the-empty-stream
      (if (= n 0)
          (stream-car s)
          (stream-ref (stream-cdr s) (- n 1)))))

(define (stream-map proc . argstreams)
  (if (stream-null? (car argstreams))
      the-empty-stream
      (cons-stream (apply proc (map stream-car argstreams))
                   (apply stream-map 
                          (cons proc (map stream-cdr argstreams))))))

(define (stream-for-each proc s)
  (if (stream-null? s)
      'done
      (begin (proc (stream-car s))
             (stream-for-each proc (stream-cdr s)))))

; Neil, 2012-05-10
(define (stream-subseq stream a b)
  (cond ((stream-null? stream) the-empty-stream)
        ((= a b) the-empty-stream)
        ((> a b) the-empty-stream)
        (else (cons-stream (stream-ref stream a)
              (stream-subseq stream (+ a 1) b)))))

(define (display-line x)
  (newline)
  (display x))

(define (display-stream s)
  (stream-for-each display-line s))

; examples
;(let ((x (delay (+ 1 2))))
;  (for ([i (in-range 1 10)])
;            (display (force x))))
;
(define (integers-starting-from n)
  (cons-stream n (integers-starting-from (+ n 1))))

(define integers
  (integers-starting-from 1))

;(display-line (stream-ref integers 0))
(let ((x (stream-subseq integers 10000 10010)))
  (display-stream x))

(define odd-numbers 
  (stream-filter odd? integers))

(display-stream (stream-subseq odd-numbers 50 60))
  
;(let ((x (cons-stream 1 (cons-stream 2 '(3)))))
;  (display-stream x))

(define (stream-add s n)
  (stream-map (lambda (x)
                (+ x n)) s))

(define (add-streams s1 s2)
  (stream-map + s1 s2))

(define fib
  (cons-stream 1
               (cons-stream 1
                            (add-streams fib
                                        (stream-cdr fib)))))

(display-stream (stream-subseq fib 150 160))


(define (divisible? x y)
  (= (remainder x y) 0))

(divisible? 10 2)

(define (sieve stream)
  (cons-stream
   (stream-car stream)
   (sieve (stream-filter
           (lambda (x)
             (not (divisible? x (stream-car stream))))
           (stream-cdr stream)))))

(define primes
  (sieve (integers-starting-from 2)))

(display-stream (stream-subseq primes 1000 1010))

接下来打算认真体会一下书中提到的欧拉发明的序列加速器的算法。真的是很厉害的 idea.

posted on 2012-05-11 01:23  NeilChen  阅读(3250)  评论(0编辑  收藏  举报

导航