我需要表达素数序列。 (在Euler项目中与前三名挣扎)。
我碰巧遇到了这个递归定义:
is_not_dividable_by :: (Integral a) => a -> a -> Bool
is_not_dividable_by x y = x `rem` y /= 0
accumulate_and :: (Integral a) => [a] -> (a -> Bool) -> Bool
accumulate_and (x:xs) (f) = (accumulate_and xs (f)) && f(x)
accumulate_and [] f = True
integers = [2,3..]
prime_sequence = [n | n <- integers, is_prime n]
where is_prime n = accumulate_and
(takeWhile (<n) (prime_sequence))
( n `is_not_dividable_by`)
result = take 20 prime_sequence
str_result = show result
main = putStrLn str_result
虽然它编译得很好但是在执行时会陷入循环,只返回<<loop>>
我的问题是我认为我可以在Haskell中自由表达递归定义。 但显然这个定义根本不符合语言。
然而,当我在心理上尝试解决prime_sequence
时,我认为我成功并且增长了序列,但当然是先于命令式编程。
我的递归定义中有什么错误,这使得这段代码在Haskell中不起作用?
答案 0 :(得分:3)
这是一个无限循环的原因是因为这一行:
prime_sequence =
[n | n <- integers, is_prime n]
where is_prime n = accumulate_and (takeWhile (< n) prime_sequence)
(n `is_not_dividable_by`)
为了计算is_prime n
,需要将所有素数小于n
。但是,为了让takeWhile
知道何时停止,还需要检查尚未计算的n
。
(以手工波浪的方式,这意味着您的prime_sequence
太懒所以它最终会biting its own tail并变成无限循环。)
以下是如何在不进入无限循环的情况下生成素数的无限列表:
-- | An infinite list of prime numbers in ascending order.
prime_sequence :: [Integer]
prime_sequence = find [] integers
where find :: [Integer] -> [Integer] -> [Integer]
find primes [] = []
find primes (n : remaining)
| is_prime = n : find (n : primes) remaining
| otherwise = find primes remaining
where is_prime = accumulate_and primes (n `is_not_dividable_by`)
此处的重要功能是find
,其中包含primes
的现有列表和remaining
整数列表,并生成 next {{1} } prime是prime,然后通过remaining
捕获它来延迟剩余的计算,直到稍后。
答案 1 :(得分:3)
罪魁祸首就是这个定义:
prime_sequence = [n | n <- [2,3..], is_prime n] where
is_prime n = accumulate_and
(takeWhile (< n) (prime_sequence))
( n `is_not_dividable_by`)
尝试找到prime_sequence
的头元素(由main
打印的20个中的第一个)导致takeWhile
需要检查prime_sequence
&#39 ; s元素。这导致takeWhile
调用需要检查prime_sequence
的头元素。它一次又一次地发生。
那是黑洞,马上就到了。 takeWhile
甚至无法开始沿着它的输入行走,因为那里还没有。
这可以通过启动序列轻松修复:
prime_sequence = 2 : [n | n <- [3,4..], is_prime n] where
is_prime n = accumulate_and
(takeWhile (< n) (prime_sequence))
( n `is_not_dividable_by`)
现在它开始工作,并遇到Rufflewind's answer中描述的第二个问题:takeWhile
无法继续沿其输入行走。最简单的解决方法是停在n/2
。但是停在sqrt会好得多:
prime_sequence = 2 : [n | n <- [3,4..], is_prime n] where
is_prime n = accumulate_and
(takeWhile ((<= n).(^ 2)) (prime_sequence))
( n `is_not_dividable_by`)
现在应该可以了。