我在过去几天开始学习Haskell,但是我遇到了这段代码的问题。我正在尝试创建一个函数,在给定初始列表(包含至少2个),最大列表长度,当前除数的索引(应从1开始,通过将当前数除以所有测试)的情况下生成素数列表到目前为止的素数)和当前要测试的数字(奇数)。
我知道它不是非常优雅或高效但是这段代码不会编译或运行所以我想在优化之前先修复它。虽然对此的建议也很酷。
primes = [2,3,5,7,11,13]
genPrimes primes max curDiv curTest
| length primes >= max = primes
| primes !! curDiv > floor . sqrt curTest = genPrimes (primes ++ [curTest]) max 1 (curTest + 2)
| curTest `mod` primes !! curDiv == 0 = genPrimes primes max 1 (curTest + 2)
| curTest `mod` primes !! curDiv /= 0 = genPrimes primes max (curDiv + 1) curTest
当我尝试编译上面的代码时出现以下错误:
Couldn't match expected type `a0 -> c0' with actual type `Integer'
Expected type: [a0 -> c0]
Actual type: [Integer]
In the first argument of `genPrimes', namely `primes'
In the expression: genPrimes primes 50 1 15
答案 0 :(得分:5)
至少你的代码应该是
primes = [2,3,5,7,11,13]
genPrimes primes max = go primes (length primes) 1 (last primes + 2)
where
go prs len d t
| len >= max = prs
| (prs !! d) > (floor . sqrt . fromIntegral) t
= go (prs++[t]) (len+1) 1 (t + 2)
| t `rem` (prs !! d) == 0 = go prs len 1 (t + 2)
| t `rem` (prs !! d) /= 0 = go prs len (d + 1) t
test n = print $ genPrimes primes n
main = test 20
然后你重新组织它(抽象出为每个候选数字执行的测试,作为noDivs
函数):
genPrimes primes max = go primes (length primes) (last primes + 2)
where
go prs len t
| len >= max = prs
| noDivs (floor . sqrt . fromIntegral $ t) t prs
= go (prs++[t]) (len+1) (t + 2)
| otherwise = go prs len (t + 2)
noDivs lim t (p:rs)
| p > lim = True
| t `rem` p == 0 = False
| otherwise = noDivs lim t rs
然后您将noDivs
重写为
noDivs lim t = foldr (\p r -> p > lim || rem t p /= 0 && r) False
然后你注意到go
只过滤了数字,通过noDivs
测试:
genPrimes primes max = take max (primes ++ filter theTest [t, t+2..])
where
t = last primes + 2
theTest t = noDivs (floor . sqrt . fromIntegral $ t) t
但是这还不行,因为theTest
需要将primes
(找到的全新素数)传递给noDivs
,但我们正在构建< / em>此whole_primes
列表(如take max (primes ++ ...)
),那么是否存在恶性循环?不,因为我们只测试数字的平方根:
genPrimes primes max = take max wholePrimes
where
wholePrimes = primes ++ filter theTest [t, t+2..]
t = last primes + 2
theTest t = noDivs (floor . sqrt . fromIntegral $ t) t wholePrimes
现在正在运作。但最后,genPrimes
现在没有什么特别的,它只是对take
的一个荣耀的调用,并且初始primes
列表实际上可以缩小,所以我们得到(更改{{{ 1}}一点点,使其界面更通用):
noDivs
现在无限期地定义全局primes = 2 : 3 : filter (noDivs $ tail primes) [5, 7..]
noDivs factors t = -- return True if the supplied list of factors is too short
let lim = (floor . sqrt . fromIntegral $ t)
in foldr (\p r-> p > lim || rem t p /= 0 && r) True factors
-- all ((/=0).rem t) $ takeWhile (<= lim) factors
-- all ((/=0).rem t) $ takeWhile ((<= t).(^2)) factors
-- and [rem t f /= 0 | f <- takeWhile ((<= t).(^2)) factors]
列表(即“无限”)。 Next step要认识到在素数的连续平方之间,要测试的因子列表的长度将是相同的,对于每个新段递增1。 Then,将所有因素预先作为全局primes
列表的前缀(已知长度),我们可以直接生成它们的倍数(因此每个因素都来自它的主要因素,而不是按顺序测试每个数字是否是其平方根以下任何一个素因子的倍数。
答案 1 :(得分:4)
你有':'反转的参数:标量向左移动,或者你可以创建一个单例列表并连接:
| primes !! curDiv > floor . sqrt curTest = genPrimes (primes++[curTest]) max 1 curTest + 2
答案 2 :(得分:1)
JA。已经给出了正确的答案,但你的解决方案并不是非常惯用的。这是生成无限素数列表的简便方法:
primes = 2 : filter isPrime [3,5..]
isPrime n = all ((/=0).(n `mod`)) $ takeWhile (\p -> p*p <= n) primes
primes
很容易理解,它将2定义为素数,并检查所有后续奇数,如果它们是素数。 isPrime
稍微复杂一点:首先,我们将所有素数小于或等于n
的平方根。然后我们检查是否将n
除以所有这些我们没有提醒等于0的素数。isPrime
引用回primes
,但这不是问题,因为Haskell是懒惰的,并且我们的支票永远不需要“太多”的素数。
列表primes
是无限的,但你可以写一些类似take 10 primes
的内容。
请注意,此代码有其自身的问题,请参阅http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf