我在互联网上找到了这个解决方案,我需要一些帮助来理解它:
isPrime' :: Integer -> Bool
isPrime' n = foldr (\x acc -> (n `rem` x) /= 0 && acc) True primes
where primes = 2 : filter isPrime' [3,5..]
有几件事:
我的理解是,如果fold函数的累加器是Boolean
,则必须在lambda函数本身中设置它。类似的东西:
(\x acc -> if (n `rem` x /= 0) then False else acc) True primes
但事实并非如此。
此外,primes
使用的范围没有终止号码。我知道这个工作的原因是因为Haskell的懒惰评估,但这究竟是如何在这里工作的?
最后,这个函数似乎不会返回正确的布尔值。素数是一个没有其他除数但本身和1的素数。所以lambda不应该读:
(\x acc -> if (n `rem` x) == 0 then False else acc) True primes
我很困惑。请帮帮我。
答案 0 :(得分:1)
至少对我来说,你的代码还没有完成。它缺少停止条件,可以在wiki:
的类似解决方案中看到isPrime n = n > 1 &&
foldr (\p r -> p*p > n || ((n `rem` p) /= 0 && r)) True primes
primes = 2 : filter isPrime [3,5..]
在这里你可以看到,当没有可能的素因子时,条件p*p > n
会产生True
。由于执行延迟,||
的正确部分未被评估,foldr
停止。
答案 1 :(得分:1)
具有
primes = [p1, p2, p3, ..., pn, ...]
计算
isPrime' n = foldr (\x acc -> (n `rem` x) /= 0 && acc) True primes
好像在计算
isPrime' n = rem n p1 /= 0 && (
rem n p2 /= 0 && (
rem n p3 /= 0 && (
..........
rem n pk /= 0 && (
.......... ..... ))))
对于复合n
这个有效 - rem
个表达式中的一个将为0,不等式为false,整个表达式也为假,因为&&
是短路的,懒惰的第二个论点。
对于素数n
,这将导致一个问题:根据素数的定义,rem
个表达式中没有一个是0,直到我们在素数中达到素数p_i == n
。但它还没有,因为我们还没有发现它是最优质的 - 我们现在正在做它。 为了测试它是否是素数,我们必须知道它是素数 - 显然是一个糟糕的情况。
primes
还需要一个尚未存在的值。这被称为“黑洞”。它会导致错误,计算中止(或卡住)。
换句话说,就像定义
一样primes = 2 : filter isPrime' [3,5..]
被定义为
primes = 2 : [ n | n <- [3,5..]
, and [rem n p /= 0 | p <- takeWhile (< n) primes]]
问题是要在n
为素数时停止,takeWhile (< n)
必须在p
中达到高于或等于n
的{{1}},但它是还没有。 “黑洞”。
着名的“筛选”代码通过“转置”工作流来解决这个问题,
primes
从而使其工作,同时保持其计算效率低下(代码的较小问题),不必要地用太多素数测试其候选者(其中每个素数由其所有前面的素数测试而不是仅仅那些不高于其平方根的那些,使它不必要地慢。
通过使用
对测试进行适当的早期停止来减轻这种情况primes = map head . iterate (\(x:xs)-> filter ((/=0).(`rem`x)) xs) $ [2..]
,根据您的定义,相当于
primes = 2 : [ n | n <- [3,5..]
, and [rem n p /= 0 | p <- takeWhile ((<= n).(^2)) primes]]
您现在可以将其写为基于isPrime' n = p1*p1 > n || rem n p1 /= 0 && (
p2*p2 > n || rem n p2 /= 0 && (
p3*p3 > n || rem n p3 /= 0 && (
..........
pk*pk > n || rem n pk /= 0 && (
.......... ..... ))))
的定义(可在此处的另一个答案中看到)。
答案 2 :(得分:0)
表达式
(n `rem` x) /= 0
显然产生了Bool结果。参数acc
已经是Bool值。所以
(n `rem` x) /= 0 && acc
是两个Bool值的逻辑AND,显然产生Bool结果。困惑在哪里?
输入列表是无限的,仅由奇数组成。只要你在检查了有限数量的值之后就能产生结果,懒惰就会使一切变好。