Haskell素数函数不起作用

时间:2011-12-25 07:36:18

标签: haskell syntax primes

我在过去几天开始学习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

3 个答案:

答案 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