primes :: [Int]
primes = sieve [2..]
sieve :: [Int] -> [Int]
sieve (n:ns) = n : sieve [n' | n' <- ns, mod n n' /= 0]
字符串
所以我得到了这个,这几行代码将生成一个无穷大的素数列表,但是我很难理解一件事,当我试图从一个无穷大的列表中过滤出所有2的倍数时,这些倍数也是无穷大的,所以我们在过滤掉2的所有倍数之前是如何进行递归调用的。因为我试图生成一个所有素数的无限列表,所以所有2的倍数需要先被去掉,然后再去找下一个质数?
2条答案
按热度按时间wgeznvg71#
filter
,像Haskell中的许多东西一样,是懒惰的。它也不会立即评估其结果。即使给定一个有限列表,它也只是“记住”它需要做什么,并且只有在被迫时才这样做。让我们看看评估的几个步骤。这是你的代码字符串
(Note:在您的版本中,
n'
和n
在mod
中是向后的;我已经冒昧地纠正了这一点)让我们假设我刚刚完成了
print (sieve [2..])
(其中print
为了打印的目的而完全强制其参数)。然后我们有
型
sieve
在其参数上进行模式匹配,因此我们需要无限参数的第一个元素。也就是说,我们必须强制第一个元素,以获得型
现在我们有足够的信息来调用
sieve
一次。让我们用右边的定义替换左边的定义。注意,n
是2
,ns
是[3..]
。型
在这一点上,我们还没有强制列表解析。它仍然只是一个美化的过滤器。在这一点上,
print
有足够的信息来打印出2
。但是现在我们需要列表的下一个元素,因为我们要把它们都打印出来。所以我们需要对sieve
进行足够远的计算,以获得下一个元素。sieve
,反过来,模式匹配它的参数,所以我们需要列表解析的第一个元素。型
现在
sieve
计算,n
等于3
,ns
等于.型
但正如我所说的,
ns
* 实际上 * 是整个列表的理解,所以这实际上是型
好了,现在让我们再看一步,看看跳过一个值是什么样子的。和前面一样的规则:我们需要另一个值,所以
sieve
需要产生一些东西,但是它需要参数的第一个元素来进行模式匹配。所以我们试图将一个元素从列表解析中强制出来。型
现在
sieve
有足够的信息来继续,所以它根据自己的定义生成5
的值,然后用一个新的参数调用自己,这个参数将由 * 三个 * 嵌套的列表解析组成。解析将继续增长。实际上,任何Haskell实现都会优化它,所以你可能最终会得到一个有多个条件的大列表解析,但这是一个优化技巧,不是非严格语义本身的一部分。
h7wcgrx32#
所以所有2的倍数在我们找到下一个质数之前都要去掉?
否,过滤也是懒惰的。实际上,过滤操作,也就是你的列表解析所做的,是这样实现的:
字符串
这也可以作为一个生成器:你可以问下一个项目,它会返回下一个不能被2,3,5等整除的项目。
因此,列表解析并不急于首先过滤掉所有的项。它传递一个生成器,该生成器将处理过滤后的项。因此,这意味着如果我们传递一个无限列表[2,3,4,.]
, it will return
2`作为第一项,并递归一个不能被2整除的所有项的列表。但是这个列表也是懒惰的。如果我们然后要求下一项,它将返回3,我们在一个生成器上递归,该生成器将另一个生成器作为输入,并将返回除以3的项。