如何在Haskell中实现素数列表,以便可以懒惰地检索它们?
我是Haskell的新手,想了解懒惰评估功能的实际用途。
答案 0 :(得分:21)
这是一个简短的Haskell函数,它枚举来自Literate Programs:
的素数primes :: [Integer]
primes = sieve (2 : [3, 5..])
where
sieve (p:xs) = p : sieve [x|x <- xs, x `mod` p > 0]
显然,这是不是 Eratosthenes的筛子(谢谢,Landei)。我认为它仍然是一个有启发性的例子,表明你可以在Haskell中编写非常优雅的短代码,并显示错误数据结构的选择如何严重损害效率。
答案 1 :(得分:18)
我建议采用本文中的一个实现:http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf
答案 2 :(得分:8)
在haskell wiki中有许多延迟生成素数序列的解决方案。第一个也是最简单的是Postponed Turner sieve:(旧版本...... NB)
primes :: [Integer]
primes = 2: 3: sieve (tail primes) [5,7..]
where
sieve (p:ps) xs = h ++ sieve ps [x | x <- t, x `rem` p /= 0]
-- or: filter ((/=0).(`rem`p)) t
where (h,~(_:t)) = span (< p*p) xs
答案 3 :(得分:1)
@nikie接受的答案效率不高,数千后变得相对缓慢,但@sleepynate的答案要好得多。我花了一些时间来理解它,因此这里是相同的代码,但只是更清楚地命名变量:
lazyPrimes :: [Integer]
lazyPrimes = 2: 3: calcNextPrimes (tail lazyPrimes) [5, 7 .. ]
where
calcNextPrimes (p:ps) candidates =
let (smallerSquareP, (_:biggerSquareP)) = span (< p * p) candidates in
smallerSquareP ++ calcNextPrimes ps [c | c <- biggerSquareP, rem c p /= 0]
主要思想是,下一个素数的候选者已经不包含任何可以被赋予该函数的第一个素数的素数整除的数字。所以,如果你打电话
calcNextPrimes (5:ps) [11,13,17..]
候选列表中不包含可被2
或3
整除的数字,这意味着第一个非素数候选者将为5 * 5
,因为5* 2
和5 * 3
和5 * 4
已被淘汰。这允许你取所有小于5的平方的候选者并将它们直接添加到素数并筛选其余的以消除所有可被5整除的数字。
答案 4 :(得分:1)
primes = 2 : [x | x <- [3..], all (\y -> x `mod` y /= 0)
(takeWhile (<= (floor . sqrt $ fromIntegral x)) primes)]
最初具有 2 的列表中,对于大于 2 的每个整数 x
,请检查是否所有 {em> y
中的primes
,这样 y <= sqrt(x)
, x mod y != 0
,这意味着 x
除了 1 及其本身之外没有其他因素。