【发布时间】:2020-07-04 15:23:54
【问题描述】:
在未来的课程中,我将学习一门使用 Python 的学科,重点是使用序列和生成器以及 Python 中的那些东西。
我一直在按照练习列表来练习这些部分。我被困在一个要求使用主要发电机的练习中。 到现在为止,我使用 Python 的还不是很多,但是我已经阅读并完成了 SICP 中的大部分练习。 在那里,他们展示了以下程序,该程序利用 Eratosthenes 的筛子生成一个惰性素数列表。
(define (sieve stream)
(cons-stream
(stream-car stream)
(sieve (stream-filter
(lambda (x)
(not (divisible? x (stream-car stream))))
(stream-cdr stream)))))
(define primes (sieve (integers-starting-from 2)))
在 Python 中,根据我的阅读,最接近的是生成器 所以我尝试将其翻译成以下内容。
import itertools
def sieve(seq):
n = next(seq)
yield n
sieve(filter(lambda x: x % n != 0, seq))
def primes():
return sieve(itertools.count(2))
print(list(itertools.islice(primes(),10)))
但它只打印[2]。
我认为这是因为对 sieve 的递归调用的结果只是被丢弃了,而不是像我最初预期的那样再次运行该函数。
为了解决这个问题,我尝试使用循环来代替:
def sieve(seq):
def divisible(n):
return lambda x: x % n != 0
while True:
n = next(seq)
yield n
seq = sieve(filter(divisible(n), seq))
在我可以生成前 9 个素数的情况下,这是可行的,但如果我要求第十个,则会引发 RecursionError。
那么,我的问题是如何改进它以便能够计算更大的素数?
PS:https://stackoverflow.com/a/568618/6571467 中已经提出了筛生成器的实现,但它明确处理筛中的先前素数。而在惰性列表范式中,目标是从操作实际执行的顺序中抽象出来。
【问题讨论】:
-
惰性范式依赖尾调用优化 (TCO) 来避免每次尝试向候选流中添加另一个过滤器时调用堆栈都增加。不幸的是,Python 没有实现 TCO。 (实际上,调用堆栈本身就是存储先前素数的东西,Python 不允许它无限制地增长。)
-
请注意,尽管存在常见的误解,但这实际上并不是埃拉托色尼的筛子。 It's a highly inefficient form of trial division.
-
相比之下,your link 中的筛子是一个真正的惰性筛子。
-
user2357112 支持 Monica 谢谢你的论文。我没有意识到在 SICP 中完成的过滤与原始筛子中的多重交叉有根本的不同。虽然我不明白显示的代码,因为我从来没有学过 Haskell,也不知道它们是做什么的。
标签: python python-3.x generator sicp lazy-sequences