Haskell主要测试

时间:2010-12-27 20:01:16

标签: haskell primes short-circuiting

我是Haskell的新手,我正在尝试一下:

isPrime :: Integer->Bool
isPrime x = ([] == [y | y<-[2..floor (sqrt x)], mod x y == 0])

我有几个问题。

  1. 为什么当我尝试加载.hs时,WinHugs说:(Floating Integer, RealFrac Integer)的实例需要isPrime的实例?
  2. 当解释器在右侧集合中找到一个元素时,它会立即停止或计算所有集合?我想你知道我的意思。
  3. 对不起我的英语。

5 个答案:

答案 0 :(得分:17)

1)问题是sqrt的类型为(Floating a) => a -> a,但您尝试使用Integer作为参数。因此,您必须先将Integer转换为浮点数,例如写sqrt (fromIntegral x)

2)我认为没有理由为什么==不应该是懒惰的,但是为了测试一个空集合你可以使用null函数(这绝对是懒惰的,因为它适用于无限列表):< / p>

isPrime :: Integer->Bool
isPrime x = null [y | y<-[2..floor (sqrt (fromIntegral x))], x `mod` y == 0]

但是为了获得更加惯用的解决方案,将问题分解为更小的子问题。首先,我们需要y * y&lt; = x:

的所有元素y的列表
takeWhile (\y ->  y*y <= x) [2..]

然后我们只需要划分x:

的元素
filter (\y ->  x `mod`y == 0) (takeWhile (\y ->  y*y <= x) [2..])

然后我们需要检查该列表是否为空:

isPrime x = null (filter (\y ->  x `mod`y == 0) (takeWhile (\y ->  y*y <= x) [2..]))

如果这看起来像你好,请用$

替换一些parens
isPrime x = null $ filter (\y ->  x `mod` y == 0) $ takeWhile (\y ->  y*y <= x) [2..]

为了更加清晰,你可以“外包”lambdas:

isPrime x = null $ filter divisible $ takeWhile notTooBig [2..] where
     divisible y = x `mod`y == 0
     notTooBig y = y*y <= x

通过将null $ filter替换为$ any:

,可以使其几乎“人类可读”
isPrime x = not $ any divisible $ takeWhile notTooBig [2..] where
     divisible y = x `mod`y == 0
     notTooBig y = y*y <= x

答案 1 :(得分:7)

  1. 因为sqrt的类型为Floating a => a -> a。这意味着输入必须是Floating类型,输出将是相同的类型。换句话说,x必须是Floating类型。但是,您声明x属于Integer类型,而不是Floating类型。另外floor需要RealFrac类型,因此x也需要这样。

    错误消息表明您通过将Integer设为Floating类型(通过定义实例Floating Integer(以及RealFrac的相同内容)来解决此问题。

    当然,在这种情况下,这不是正确的方法。相反,您应该使用fromIntegralx转换为RealFloatingRealFrac的实例),然后将其转换为sqrt }。

  2. 是。只要==看到右操作数至少有一个元素,它就知道它不等于[],因此返回False

    话虽如此,null是一种更为惯用的方式来检查列表是否为空[] ==

答案 2 :(得分:1)

关于第二点,它会停止,例如:

[] == [x | x <- [1..]]

返回False

答案 3 :(得分:1)

Landei的解决方案很棒,但是,如果你想要一个更高效的¹实现(感谢BMeph):

-- list of all primes
primes :: [Integer]
primes = sieve (2 : 3 : possible [1..]) where
     sieve (p : xs) = p : sieve [x | x <- xs, x `mod` p > 0]
     possible (x:xs) = 6*x-1 : 6*x+1 : possible xs

isPrime :: Integer -> Bool
isPrime n = shortCircuit || (not $ any divisible $ takeWhile inRangeOf primes) where
    shortCircuit = elem n [2,3] || (n < 25 && ((n-1) `mod` 6 == 0 || (n+1) `mod` 6 == 0))
    divisible y = n `mod` y == 0
    inRangeOf y = y * y <= n

'效率'来自使用常数素数。它以两种方式改进了搜索:

  1. Haskell运行时可以缓存结果,因此不会评估后续调用
  2. 它通过逻辑消除了一系列数字 请注意,sieve值只是一个递归表,其中表示 列表是素数,并将其添加到它。对于其余的列表,如果没有 列表中已有的其他值组成了数字,然后它也是素数 possible是所有可能素数的列表,因为所有可能的素数都在 除2和3外,形成6 * k-1或6 * k-1 同样的规则也适用于shortCircuit,以便快速摆脱计算
  3. D.F.的脚注
    ¹找到素数仍然是一种非常低效的方法。如果您需要大于几千的质数,请不要使用试验区,而是使用筛子。 far more上有几个efficient hackage个实现。

答案 4 :(得分:-2)

  1. 我认为WinHugs需要为Integer等导入一个模块...试试Int
  2. 在您致电之前,翻译人员不会计算任何内容isPrime 32然后它将懒惰地计算表达式。
  3. PS你的isPrime实现不是最好的实现!