In 3.5.1, we showed how to implement streams as delayed lists.
We introduced special forms delay and cons-stream, which allowed
us to construct a “promise” to compute the cdr of a stream, without
actually fulfilling that promise until later. We could use this general
technique of introducing special forms whenever we need more control over the
evaluation process, but this is awkward. For one thing, a special form is not
a first-class object like a procedure, so we cannot use it together with
higher-order procedures. Additionally, we were
forced to create streams as a new kind of data object similar but not identical
to lists, and this required us to reimplement many ordinary list operations
(map, append, and so on) for use with streams.
在 3.5.1 节中,我们展示了如何将流实现为延迟表。我们引入了特殊形式 `delay` 和 `cons-stream`,它们允许我们构造一个“承诺”:承诺计算流的 `cdr`,而不必当场兑现,直到需要时才履行。我们本可以在每次需要对求值计算过程进行更多控制时,都采用这种引入特殊形式的通用技术,但这样做很不方便。原因之一是,特殊形式不像过程那样是第一类对象 (first-class object),因此不能与高阶过程 (higher-order procedures) 配合使用。此外,我们被迫将流作为一种新的数据对象来创建——它与表相似却并不相同——这使得我们不得不重新实现许多普通的表操作(如 `map`、`append` 等)以供流使用。
With lazy evaluation, streams and lists can be identical, so there is no need
for special forms or for separate list and stream operations. All we need to
do is to arrange matters so that cons is non-strict. One way to
accomplish this is to extend the lazy evaluator to allow for non-strict
primitives, and to implement cons as one of these. An easier way is to
recall (2.1.3) that there is no fundamental need to implement
cons as a primitive at all. Instead, we can represent pairs as
procedures:
在惰性求值下,流与表可以完全统一,从而无需特殊形式,也无需分别维护表操作和流操作。我们所需要做的,只是让 `cons` 成为非严格的。实现这一点的一种途径,是扩展惰性求值器以支持非严格基本过程,并将 `cons` 实现为其中之一。还有一种更简便的方法——回想 2.1.3 节的内容——其实根本无需将 `cons` 实现为基本过程。我们可以用过程来表示序对:
In terms of these basic operations, the standard definitions of the list
operations will work with infinite lists (streams) as well as finite ones, and
the stream operations can be implemented as list operations. Here are some
examples:
基于这些基本操作,表操作的标准定义对无限表(即流)和有限表同样适用,流操作也可以作为表操作来实现。以下是一些示例:
Note that these lazy lists are even lazier than the streams of Chapter 3:
The car of the list, as well as the cdr, is
delayed. In fact, even accessing the car or
cdr of a lazy pair need not force the value of a list element. The
value will be forced only when it is really needed—e.g., for use as the
argument of a primitive, or to be printed as an answer.
注意,这些惰性表比第 3 章的流还要“懒”:表的 `car` 和 `cdr` 都被延迟了。事实上,即使访问惰性序对的 `car` 或 `cdr`,也不必强迫表元素的值。这个值只有在真正需要时才会被强迫——例如,作为某个基本过程的实参,或者作为答案被打印出来时。
Lazy pairs also help with the problem that arose with streams in
3.5.4, where we found that formulating stream models of systems with
loops may require us to sprinkle our programs with explicit delay
operations, beyond the ones supplied by cons-stream. With lazy
evaluation, all arguments to procedures are delayed uniformly. For instance,
we can implement procedures to integrate lists and solve differential equations
as we originally intended in 3.5.4:
惰性序对还有助于解决 3.5.4 节中流所遇到的那个问题——在那里我们发现,对带有循环的系统建立流模型,可能需要在程序中添加显式的 `delay` 操作,超出 `cons-stream` 所提供的范围。而有了惰性求值,过程的所有实参都会被均匀地延迟。例如,我们可以按照 3.5.4 节中最初的构想,实现对表进行积分并求解微分方程的那些过程:
(define (cons x y) (lambda (m) (m x y)))
(define (car z) (z (lambda (p q) p)))
(define (cdr z) (z (lambda (p q) q))) (define (list-ref items n)
(if (= n 0)
(car items)
(list-ref (cdr items) (- n 1))))
(define (map proc items)
(if (null? items)
'()
(cons (proc (car items))
(map proc (cdr items)))))
(define (scale-list items factor)
(map (lambda (x) (* x factor))
items))
(define (add-lists list1 list2)
(cond ((null? list1) list2)
((null? list2) list1)
(else (cons (+ (car list1)
(car list2))
(add-lists
(cdr list1)
(cdr list2))))))
(define ones (cons 1 ones))
(define integers
(cons 1 (add-lists ones integers)))
;;; L-Eval input:
(list-ref integers 17)
;;; L-Eval value:
18 (define (integral integrand initial-value dt)
(define int
(cons initial-value
(add-lists (scale-list integrand dt)
int)))
int)
(define (solve f y0 dt)
(define y (integral dy y0 dt))
(define dy (map f y))
y)
;;; L-Eval input:
(list-ref (solve (lambda (x) x) 1 0.001) 1000)
;;; L-Eval value:
2.716924