SICP“;“作为信号的流”;用Python
我发现了一些在Python中实现类SICP流的好例子(,)。但是我仍然不知道如何处理像SICP 3.5.3中的SICP“;“作为信号的流”;用Python,python,stream,sicp,Python,Stream,Sicp,我发现了一些在Python中实现类SICP流的好例子(,)。但是我仍然不知道如何处理像SICP 3.5.3中的积分这样的例子 在那里找到的方案代码为 (define (integral integrand initial-value dt) (define int (cons-stream initial-value (add-streams (scale-stream integrand dt)
积分这样的例子
在那里找到的方案代码为
(define (integral integrand initial-value dt)
(define int
(cons-stream initial-value
(add-streams (scale-stream integrand dt)
int)))
int)
关于这个问题,需要技巧的是返回的流int
是根据自身定义的(即,流int
用于流int
的定义)
我相信Python可以有类似的表现力和简洁性。。。但不知道怎么做。所以我的问题是,Python中类似的stream-y构造是什么?(我所说的流是SICP中3.5的主题,但简单地说,是一种结构(如Python生成器),它返回不定长序列的连续元素,并且可以与诸如添加流和缩放流之类的操作组合和处理,这些操作尊重流的惰性特征。)阅读问题有两种方法。第一个问题很简单:如何使用流结构,可能是第二个结构中的流结构,但是使用递归定义?这是可以做到的,尽管在Python中有点笨拙
在Python中,可以表示循环数据结构,但不能直接表示。你不能写:
l = [l]
def integral(integrand,initial_value,dt):
int_rec = cons_stream(initial_value,
add_streams(scale_stream(integrand,dt),
int_rec))
return int_rec
但你可以写:
l = [None]
l[0] = l
def integral(integrand,initial_value,dt):
placeholder = Stream(initial_value,lambda : None)
int_rec = cons_stream(initial_value,
add_streams(scale_stream(integrand,dt),
placeholder))
placeholder._compute_rest = lambda:int_rec
return int_rec
同样,你不能写:
l = [l]
def integral(integrand,initial_value,dt):
int_rec = cons_stream(initial_value,
add_streams(scale_stream(integrand,dt),
int_rec))
return int_rec
但你可以写:
l = [None]
l[0] = l
def integral(integrand,initial_value,dt):
placeholder = Stream(initial_value,lambda : None)
int_rec = cons_stream(initial_value,
add_streams(scale_stream(integrand,dt),
placeholder))
placeholder._compute_rest = lambda:int_rec
return int_rec
请注意,我们需要笨拙地预先计算占位符的第一个元素,然后只修复流其余部分的递归。但这一切都是可行的(与所有其他代码的适当定义一起——我将把它全部放在这个答案的底部)
然而,问题的第二部分似乎是问如何在Python中自然地做到这一点。您需要一个“Python中的类似stream-y构造”。显然,答案正是发电机。生成器自然会提供流概念的惰性评估。它的不同之处在于没有自然地递归表示,但Python不支持这种方式,正如我们将看到的,Scheme也不支持这种方式
换句话说,严格的流概念可以用Python表达(如链接和上面的内容),但惯用的方法是使用生成器
通过将流直接机械转换为生成器(但避免内置int
),或多或少可以复制方案示例:
这里唯一棘手的事情是要认识到,您需要急切地调用递归使用int\u rec
作为add\u streams
的参数。调用它并不会让它开始产生值——它只会创建生成器,在需要时惰性地产生值
这对于小的被积函数非常有效,尽管它不是很适合python。Scheme版本通过优化尾部递归来工作-如果被积函数太长,Python版本将超过最大堆栈深度。因此,这在Python中并不真正合适
我认为,一个直接而自然的python版本应该是这样的:
def integral(integrand,initial_value,dt):
value = initial_value
yield value
for x in integrand:
value += dt * x
yield value
这可以有效且正确地将被积函数
惰性地视为一个“流”。然而,它使用迭代而不是递归来解包被积函数iterable,这更像Python的方式
在转向自然Python的过程中,我还删除了流组合函数——例如,将add_streams
替换为+=
。但如果我们想要一种中途之家的版本,我们仍然可以使用它们:
def accum(initial_value,a):
value = initial_value
yield value
for x in a:
value += x
yield value
def integral_hybrid(integrand,initial_value,dt):
for x in accum(initial_value,scale_stream(integrand,dt)):
yield x
该混合版本使用来自方案的流组合,并且仅避免尾部递归。这仍然是pythonic,python在itertools
模块中包含了各种其他很好的方法来使用iterables。正如你所说,他们都“尊重streams的懒惰性格”
最后,这里是第一个递归流示例的所有代码,大部分代码取自Berkeley参考:
class Stream(object):
"""A lazily computed recursive list."""
def __init__(self, first, compute_rest, empty=False):
self.first = first
self._compute_rest = compute_rest
self.empty = empty
self._rest = None
self._computed = False
@property
def rest(self):
"""Return the rest of the stream, computing it if necessary."""
assert not self.empty, 'Empty streams have no rest.'
if not self._computed:
self._rest = self._compute_rest()
self._computed = True
return self._rest
def __repr__(self):
if self.empty:
return '<empty stream>'
return 'Stream({0}, <compute_rest>)'.format(repr(self.first))
Stream.empty = Stream(None, None, True)
def cons_stream(a,b):
return Stream(a,lambda : b)
def add_streams(a,b):
if a.empty or b.empty:
return Stream.empty
def compute_rest():
return add_streams(a.rest,b.rest)
return Stream(a.first+b.first,compute_rest)
def scale_stream(a,scale):
if a.empty:
return Stream.empty
def compute_rest():
return scale_stream(a.rest,scale)
return Stream(a.first*scale,compute_rest)
def make_integer_stream(first=1):
def compute_rest():
return make_integer_stream(first+1)
return Stream(first, compute_rest)
def truncate_stream(s, k):
if s.empty or k == 0:
return Stream.empty
def compute_rest():
return truncate_stream(s.rest, k-1)
return Stream(s.first, compute_rest)
def stream_to_list(s):
r = []
while not s.empty:
r.append(s.first)
s = s.rest
return r
def integral(integrand,initial_value,dt):
placeholder = Stream(initial_value,lambda : None)
int_rec = cons_stream(initial_value,
add_streams(scale_stream(integrand,dt),
placeholder))
placeholder._compute_rest = lambda:int_rec
return int_rec
a = truncate_stream(make_integer_stream(),5)
print(stream_to_list(integral(a,8,.5)))
类流(对象):
“”“延迟计算的递归列表。”“”
def uu init uu(self,first,compute_rest,empty=False):
self.first=第一
self.\u compute\u rest=compute\u rest
self.empty=空
self.\u rest=无
self.\u computed=False
@财产
def休息(自我):
“”“返回流的其余部分,必要时进行计算。”“”
断言not self.empty,“空流无息”
如果不是自行计算的:
self.\u rest=self.\u compute\u rest()
self.\u computed=True
回归自我
定义报告(自我):
如果self.empty:
返回“”
返回'Stream({0},)'。格式(repr(self.first))
Stream.empty=流(无、无、真)
def cons_流(a、b):
回流(a,λ:b)
def添加_流(a、b):
如果a.empty或b.empty:
返回流为空
def compute_rest():
返回添加流(a.rest,b.rest)
返回流(a.first+b.first,compute\u rest)
def比例\ U流(a,比例):
如果a.empty:
返回流为空
def compute_rest():
返回比例\流(a.静止,比例)
回流(a.第一个*比例,计算剩余)
def生成整数流(第一个=1):
def compute_rest():
返回生成整数流(第一个+1)
返回流(首先,计算\u rest)
def截断_流(s,k):
如果s.empty或k==0:
返回流为空
def compute_rest():
返回截断流(s.rest,k-1)
返回流(s.first,compute_rest)
def流到列表:
r=[]
虽然不是空的:
r、 附加(s.first)
s=s.rest
返回r
def积分(被积函数,初始_值,dt):
占位符=流(初始值,λ:无)
int_rec=cons_流(初始值,
添加_流(标度_流(被积函数,dt),
占位符)
占位符.\u compute\u rest=lambda:int\u rec
返回整数记录
A.