【问题标题】:Haskell prime number function not workingHaskell素数函数不起作用
【发布时间】:2011-12-25 07:36:17
【问题描述】:

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

【问题讨论】:

  • 除了给出错误的代码之外,发布确切的错误消息通常是个好主意。这让我们更容易为您提供帮助。

标签: haskell syntax primes


【解决方案1】:

至少你的代码应该是

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,但我们正在构建这个@987654334 @list(如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 列表实际上可以缩小,所以我们得到(稍微改变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]

全局 primes 列表现在已无限期定义(即“无限”)。 Next step 是意识到在素数的连续平方之间,要测试的因素列表的长度将是相同的,每个新段增加 1。 Then,将所有因子作为全局 primes 列表的前缀(已知长度),我们可以直接生成它们的倍数(因此每个因子都是从其主要因子生成的) ,而不是按顺序测试每个数字是否是其平方根以下的任何一个质因数的倍数。

【讨论】:

  • 是的,它好多了,谢谢!!我一直在寻找一种通过递归隐藏那些“帮助”变量的方法——我已经多次遇到这个问题。
【解决方案2】:

你已经把':'的参数颠倒了:标量向左移动,或者你可以创建一个单例列表并连接:

| primes !! curDiv > floor . sqrt curTest = genPrimes (primes++[curTest]) max 1 curTest + 2

【讨论】:

  • 好的,谢谢,不知道。将其更改为连接列表,但现在出现运行时错误.. 编辑了我的原始帖子。
【解决方案3】:

贾。已经给出了正确的答案,但是您的解决方案不是很惯用。这是生成无限素数列表的简单方法:

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

【讨论】:

  • 这是有道理的,尽管 lambda 表达式对我来说仍然有点吓人。但我不明白为什么它的运行速度比我的代码快一些,因为它本质上是在做同样的事情。我是否正确理解 (n mod) 只是一个输入的函数?如果不明确定义输入,你怎么能做到这一点?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多