sicp中stream的python实现

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

源码地址:https://github.com/Firkraag/stream-implemetation

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值