【问题标题】:Haskell prime number generator errorHaskell素数生成器错误
【发布时间】:2026-01-22 16:25:02
【问题描述】:

我试过了:

composites=[c|c<-[4..], any (\p->(c`mod`p == 0)) (takeWhile (< (sqrt c)) primes)]
primes=2:[p|p<-[3..], not p `elem` (takeWhile (<p) composites)]

得到:

pad.hs:1:19:
    No instance for (Num Bool) arising from the literal `4'
    Possible fix: add an instance declaration for (Num Bool)
    In the expression: 4
    In the expression: [4 .. ]
    In a stmt of a list comprehension: c <- [4 .. ]

pad.hs:1:30:
    No instance for (Integral Bool) arising from a use of `divisible'
    Possible fix: add an instance declaration for (Integral Bool)
    In the first argument of `any', namely `(divisible c)'
    In the expression: any (divisible c) (factors c)
    In a stmt of a list comprehension: any (divisible c) (factors c)

pad.hs:3:43:
    No instance for (Floating Bool) arising from a use of `sqrt'
    Possible fix: add an instance declaration for (Floating Bool)
    In the second argument of `(<)', namely `sqrt c'
    In the first argument of `takeWhile', namely `(< sqrt c)'
    In the expression: takeWhile (< sqrt c) primes
Failed, modules loaded: none.

我认为它正在处理什么类型的数字感到困惑,但我不确定。有什么建议吗?

【问题讨论】:

  • 即使有来自leftaroundabouts 答案的修复,您仍然无法获得您希望的结果。要了解原因,请考虑您的 primes 不可能只是未经过滤的 [3..] 列表。您在生成 composites 时也会犯该错误
  • 在两个takeWhile 调用中应该是&lt;=,而不是&lt;

标签: haskell compiler-errors primes


【解决方案1】:

我认为它正在处理什么类型的数字感到困惑

完全正确!那你为什么不告诉它?在实际实现之前,在 Haskell 中编写函数签名总是一个好主意。这不仅可以防止出现错误时出现这种令人困惑的编译器消息,而且还可以为实际设计函数提供一个非常好的指南。

所以在你的情况下,你可能想要1

composites, primes :: [Integer]

这当然不能解决问题,但它使错误消息更加清晰:

Prelude> 让composites,primes::[Integer];复合材料=[c|c c`mod`p == 0) (takeWhile (
:2:128:
无法将预期类型“整数”与实际类型“布尔”匹配
在表达式中:p
在`(:)'的第二个参数中,即
`[p | p 在表达式中:
2:[p| p
:2:169:
无法将类型“整数”与“布尔”匹配
预期类型:[布尔]
实际类型:[Integer]
在 `takeWhile' 的第二个参数中,即 `composites'
在 `elem' 的第二个参数中,即
`(takeWhile ( 在表达式中:not p `elem`(takeWhile (

它仍然不完全正确,但至少它现在将错误定位到它所在的位置:在primes 中,p 被推断为Bool,这当然是错误的。 bool 的原因是你有not p `elem` (...) 在那里。显然您认为这被解析为not (p`elem`(...)),但事实并非如此:普通前缀函数应用程序的优先级高于任何中缀运算符。需要了解的重要事项(这也是为什么您不需要在 sqrt c 周围加上括号的原因 (&lt; sqrt c))。

让我们解决这个问题,那么还有一个问题:

Prelude> 让composites,primes::[Integer];复合材料=[c|c(c`mod`p == 0)) (takeWhile (
:3:99:
没有使用 `sqrt' 产生的 (Floating Integer) 实例
可能的解决方法:为 (Floating Integer)
添加实例声明 在`( 在 `takeWhile' 的第一个参数中,即 `( 在 `any' 的第二个参数中,即
`(takeWhile (

现在是正确的:您正在处理整数,但 sqrt 显然会产生一般无理数,因此它仅对 Floating 类型有意义。为了解决这个问题,您可以使用(虽然很丑,但还可以)sqrt' = round . sqrt . fromIntegral


1实际上,这种单态签名可能并不理想——您可能出于各种原因(主要是效率)更喜欢Int。为了安全起见,我们会选择Integral a =&gt; [a];然而,多态值并没有在顶层“记忆”,这在这个例子中又是一个相当大的问题。

【讨论】:

    【解决方案2】:

    有什么建议吗?

    好吧,既然你已经修好了,

    composites=[c|c<-[4..], any ((==0).(c`mod`)) (takeWhile ((<= c).(^2)) primes)]
    primes=2:[p|p<-[3..], not (p `elem` takeWhile (<= p) composites)]
    

    考虑是否可以加快速度。一方面,elem 在第一个参数不存在时完全使用它的第二个参数,并且总是从头开始搜索。但是如果我们只是在复合中搜索了16,那么搜索17时就不需要重新开始了。 strong> 接下来 - 我们可以从同一点继续:

    notAmong (_, (k:ks,c:cs))              -- a candidate that is not among
       | k == c    = (Nothing, (ks,cs))    --     the composites, is a prime
       | otherwise = (Just k, (ks,c:cs))   -- (k < c)
    primes = 2 : [p | (Just p,_) <- iterate notAmong (Just 3, ([4..],composites))]
    

    我们融合了函数notelemtakewhile,在速度方面有很大的好处。

    试着找出这个版本的empirical orders of growth,和原版比较,很好玩。

    【讨论】:

    • 我是怎么做到的,我将素数和合成物融合在一起。
    • @PyRulez 你的版本是否在 ~ n^1.30..1.45 上运行(在 n 产生的素数中)?
    • 取决于,ghc 会缓存它在列表中找到的任何值,还是会在每次需要时重新计算值?
    • @PyRulez 你可以凭经验测量它。链接在答案中。 :)