python的generator可以基本模拟sicp中的stream, 本文所有的stream实际上都是generator。
基本函数
返回stream的第n个值
def streamRef(stream, n):
while n > 0:
value = stream.next()
n -= 1
return value
返回stream=proc(stream1, stream2, stream3, …)
def streamMap(proc, *streams):
while True:
args = map(lambda stream : stream.next(), streams)
yield proc(*args)
用筛选器pred筛选stream的元素
def streamFilter(pred, stream):
while True:
value = stream.next()
if pred(value):
yield value
缩放stream
def scaleStream(stream, factor):
while True:
yield stream.next() * factor
返回两个stream的和
def addStreams(stream1, stream2):
return streamMap(int.__add__, stream1, stream2)
返回stream的部分和
def partialSums(stream):
sum = stream.next()
yield sum
while True:
sum += stream.next()
yield sum
应用
从n开始的自然数
def integersStartingFrom(n):
while True:
yield n
n += 1
自然数
integers = integersStartingFrom(1)
偶数
even = streamFilter(lambda x: x % 2 == 0, integers)
筛法
def sieve(stream):
while True:
value = stream.next()
yield value
stream = streamFilter(lambda x, value = value: x % value != 0, stream)
有一点需要注意,lambda中的变量value应该是某次迭代的value,所以要用lambda的parameter传入当时的value;如果不传入当时的value, 后面调用lambda函数时,在sieve的scope寻找value的值,那时的value和定义lambda函数时的value已经不同了。
筛法求素数序列
primes = sieve(integersStartingFrom(2))
根据公式pi/4 = 1 - 1/3 + 1/5 - 1/7 + …. 计算pi值
def piSummands(n):
sign = 1
yield 1.0 / n
while True:
n += 2
sign *= -1
yield sign * 1.0 / n
piStream = scaleStream(partialSums(piSummands(1)), 4)
根据公式ln2 = 1 - 1/2 + 1/3 - 1/4 + …. 计算ln2
def lnSummands(n):
sign = 1
yield 1.0 / n
while True:
n += 1
sign *= -1
yield sign * 1.0 / n
lnStreams = partialSums(lnSummands(1))
假如数列收敛于某一个值,经过欧拉变换后的新数列会更快地收敛于同一值。当denom = 0时,stream中相连的三个元素已经足够接近,这时我们认为已经得到足够精度的收敛值,可以结束欧拉变换了。
def eulerTransform(stream):
value1 = stream.next()
value2 = stream.next()
while True:
value3 = stream.next()
denom = value1 - 2 * value2 + value3
if denom == 0:
raise StopIteration
else:
yield value3 - (value3 - value2) ** 2 / denom
value1, value2 = value2, value3
对变换数列再做一次变换,会比新数列更快的收敛于同一值,所以我们可以不断地做欧拉变换,使收敛速度越来越快。所以有如下的stream of stream
def makeTableau(transform, stream):
yield stream
while True:
stream = transform(stream)
yield stream
stream of stream的一个应用。返回值的每一个元素都是stream的第一个元素。因为transform的收敛速度要快于原stream,所以transform的第一个元素要比原stream的第二个元素更接近收敛值。所以accleratedSequence的收敛速度要快于任意单一的transform。根据sicp,(accleratedSequence eulerTransform pi-streams)
的收敛速度比pi-streams快十三个数量级。
def accleratedSequence(transform, stream):
return streamMap(lambda stream: stream.next(), makeTableau(transform, stream))
在ipython上的运行结果
In [49]: for value in accleratedSequence(eulerTransform, piStream):
....: print value
....:
3.14159335263
3.14159265359
3.14159265359
In [50]: for value in accleratedSequence(eulerTransform, lnStreams):
....: print value
....:
1.0
0.690476190476
0.693163340724
0.693147111916
0.69314718078
0.693147180559
0.69314718056
0.69314718056
0.69314718056