首页 > 代码库 > Racket 模拟SICP的流(延时计算)

Racket 模拟SICP的流(延时计算)

默认的Racket是要对函数参数进行求值的, 例如(f 1 (+ 1 2))里面,(+ 1 2)要先求值为3,变为(f 1 3)再进行下一步操作.因此, Racket若按照SICP使用define关键字来定义延时计算的关键函数delay和cons-stream是不可行的, 需要用宏来定义,绕过求值.

#lang racket(define (memo-proc proc)  (let ((already-run? #f) (result #f))    (lambda ()      (if already-run?          result          (begin (set! result (proc))                 (set! already-run? #t)                 result)))))(define-syntax-rule (delay exp)     (memo-proc (lambda () exp)))(define-syntax-rule (cons-stream a b)   (cons a (delay b)))

这里memo-proc是用于避免重复计算延时过程的. 比如首次调用延时过程(+ 1 2 3), 我们得到了结果6, 那么如果还需要反复调用它1万次, 显然再进行1万次(+ 1 2 3)的计算是不明智的,只需记住首次的结果, 以后再被调用时直接用它就可以了. 但这也说明了延时过程需要是无副作用的.

下面是其他延时函数的定义:

(define-syntax-rule (force delayed-object)  (delayed-object))(define (stream-car stream) (car stream))(define (stream-cdr stream) (force (cdr stream)))(define (stream-ref s n)  (if (= n 0)      (stream-car s)      (stream-ref (stream-cdr s) (- n 1))))(define (stream-map proc s)  (if (null? s)      ()      (cons-stream (proc (stream-car s))                   (stream-map proc (stream-cdr s)))))(define (stream-for-each proc s)  (if (null? s)      done      (begin (proc (stream-car s))             (stream-for-each proc (stream-cdr s)))))(define (stream-enumerate-interval low high)  (if (> low high)      ()      (cons-stream       low       (stream-enumerate-interval (+ low 1) high))))(define (display-line x)  (newline)  (display x))(define (display-stream s)  (stream-for-each display-line s))(define (show x)  (display-line x)  x); test begins...(define x (stream-map show (stream-enumerate-interval 0 10)))(stream-ref x 5)(stream-ref x 7)

感慨: 其实我这里的宏只是相对于C语言里的那种文本替换宏. Racket的宏还能更加复杂. Scheme系语言的表达能力确实强悍, 但或许不太适合团队. 一是S表达式的形式繁琐, 语法简单优雅的代价就是视觉上的反人类,不利于交流. 二是这种语言可定制性太强, 你喜欢这样搞, 我还喜欢那样搞呢, 谁愿去理解你那些说不定你自己过段时间都理解不了的各种宏或延续的奇技淫巧呢.  此时对比Python, 其设计者的良苦用心可见一斑.

Racket 模拟SICP的流(延时计算)