我正在尝试使用列表推导尽可能简洁地找到小于某个整数n
的所有素数。我正在学习Haskell,这只是一个练习。我想写一些类似的东西:
isqrt :: Integral a => a -> a
isqrt = floor . sqrt . fromIntegral
primes :: Integral a => a -> [a]
primes n = [i | i <- [1,3..n], mod i k /= 0 | k <- primes (isqrt i)]
当然不起作用。有没有办法在列表理解中使用列表理解?
这是我得到的错误:
exercise-99-1.hs:138:39: Not in scope: `k'
exercise-99-1.hs:138:46:
Illegal parallel list comprehension: use -XParallelListComp
exercise-99-1.hs:138:68: Not in scope: `i'
但是 - 我真的不希望语法甚至是合法的: - )
目的是尽可能直接翻译:“primes n
=奇数整数i
小于n
的集合,使i
不可分割任何k
,对于集合中的所有k
:primes (isqrt i)
“ - 或多或少。 (我希望我做对了吗?)
谢谢!
答案 0 :(得分:0)
我在以下方面取得了一些进展:
primes :: Integral a => a -> [a]
primes 2 = [2]
primes n = 2:[i | i <- [3,5..n], all (\k -> if (mod i k /= 0) then True else False)
(primes (isqrt i))]
是否有更短的方法来编写lambda谓词?
编辑:是的,感谢评论中的评论!
primes :: Integral a => a -> [a]
primes 2 = [2]
primes n = 2:[i | i <- [3,5..n], all ((/= 0) . mod i) (primes (isqrt i))]
答案 1 :(得分:0)
你的代码,
primes n = [i | i <- [1,3..n], mod i k /= 0
| k <- primes (isqrt i)]
被解释为 parallel 列表理解;即,不是以通常的嵌套方式组合两个生成器,而是将它们并行组合:
primes n = [i | (i,k) <- zip [i | i <- [1,3..n], mod i k /= 0]
-- not in scope: k ^
[k | k <- primes (isqrt i)] ]
-- not in scope: i ^
相反,为了表达你的意图,它可以写成
primes 1 = []
primes n = 2:[i | i <- [3,5..n], and [mod i k /= 0 | k <- primes (isqrt i)]]
因此在列表理解中具有列表理解 - 但作为警卫的一部分,而不是发电机。函数and :: [Bool] -> Bool
使这成为可能。
顺便说一下,为每个新候选人primes
递归调用i
会使其运行缓慢,运行时间增长得太快,empirically:
> length $ primes 10000 -- => 1229 (0.50 secs)
> length $ primes 20000 -- => 2262 (1.40 secs)
> logBase (2262/1229) (1.4/0.5) -- => 1.6878 ~ n^1.69
> length $ primes 40000 -- => 4203 (4.07 secs)
> logBase (4203/2262) (4.07/1.4) -- => 1.7225 ~ n^1.72
最佳试验分区通常在 ~n 1.4..1.45 ,基于列表的Eratosthenes筛,〜 1.2..1.25 < / sup> ,如果在可变数组上最佳实现,则在 ~n 1.0..1.1 ( n 的数量为产生的素数,不上限。)