Haskell不评估Lazily takeWhile

时间:2016-08-08 10:43:32

标签: haskell list-comprehension primes lazy-evaluation prime-factoring

isqrt :: Integer -> Integer
isqrt = floor . sqrt . fromIntegral

primes :: [Integer]
primes = sieve [2..] where
 sieve (p:ps) = p : sieve [x | x <- ps, x `mod` p > 0]

primeFactors :: Integer -> [Integer]
primeFactors n = takeWhile (< n) [x | x <- primes, n `mod` x == 0]

这是我的代码。我想你猜对了我要做的事情:使用无限素数列表给定数字的素数因子列表。但是这段代码并没有懒惰地评估。

当我使用ghci:l mycode.hs并输入primeFactors 24时,结果为[2, 3(并且光标在那里不断闪烁)不再有{{1}提示。我认为那里有一个问题。我做错了什么?

感谢。

3 个答案:

答案 0 :(得分:7)

takeWhile永远不会终止复合参数。如果n是复合的,则它没有素因子>= n,因此takeWhile只会坐在那里。

takeWhile应用于素数列表,然后使用n mod x过滤结果,如下所示:

primeFactors n = [x | x <- takeWhile (<= n) primes, n `mod` x == 0]

<=代替<以获得最大正确性,因此素数的素因子将由该数字组成。)

答案 1 :(得分:1)

说明会发生什么:

http://sketchtoy.com/67338195

答案 2 :(得分:1)

您的问题不是直接takeWhile,而是列表理解。

[x | x <- primes, n `mod` x == 0]

对于n = 24,我们得到24 `mod` 2 == 024 `mod` 3 == 0,因此此列表理解的值以2 : 3 : ...开头。但请考虑...部分。

列表理解必须不断提取primes的值并检查24 `mod` x == 0。由于24没有更多的素因子,因此无法通过该测试,并将其作为列表推导的第三个值发出。但由于总是有另一个要测试的素数,它永远不会停止并得出结论,列表的剩余尾部是空的。

因为这是懒惰评估,如果您只询问此列表的前两个元素,那么您就可以了。但是如果你的程序需要第三个(甚至只是知道是否第三个​​元素),那么列表理解将永远旋转,试图想出一个。

takeWhile (< 24)不断从其参数中提取元素,直到找到不是< 24的元素。 23都通过了该测试,因此takeWhile (< 24) 需要知道列表理解的第三个元素是什么。

takeWhile并不是真正的问题;问题是你已经写了一个列表理解来找到所有的素数因子(没有别的),然后尝试在结果上使用过滤器来切断无限探索可能不是因素的所有较高素数。如果你停下来思考它,那真的没有意义;根据定义,任何不是主要因素的东西都不能成为该列表的元素,因此您无法从该列表中过滤出大于n的非因子。相反,您需要将输入过滤到该列表理解中,以便它不会尝试探索无限空间,正如@n.m的答案所示。