[英]Haskell Does Not Evaluate Lazily takeWhile
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
(并且光标一直在那里闪烁),没有其他Prelude>
提示。 我认为那里是一个问题。 我究竟做错了什么?
谢谢。
takeWhile
永远不会因复合参数而终止。 如果n
是复合的,则它没有素数>= n
,因此takeWhile
只会坐在那里。
将takeWhile
应用于素数列表,然后使用n mod
x过滤结果,如下所示:
primeFactors n = [x | x <- takeWhile (<= n) primes, n `mod` x == 0]
(使用<=
代替<
以获得最大的正确性,因此素数的素数将由该数组成)。
举例说明发生了什么:
您的问题不是直接takeWhile
,而是列表理解。
[x | x <- primes, n `mod` x == 0]
对于n = 24
,我们得到24 `mod` 2 == 0
和24 `mod` 3 == 0
,因此此列表理解的值从2 : 3 : ...
。 但是请考虑...
部分。
列表理解必须保持从primes
提取值并检查24 `mod` x == 0
。 由于不再有24
素数因子,因此没有任何东西会通过该测试并作为列表理解的第三个值发出。 但是,由于总是有另一个质数要测试,因此它将永远不会停止并得出结论,列表的其余尾部为空。
因为这是懒惰的评估,所以如果您只要求列出此列表的前两个元素,那么您就可以了。 但是,如果你的程序需要有史以来第三个(甚至只是为了知道是否有第三个要素),那么列表解析会就冒永远试图想出一个。
takeWhile (< 24)
一直从其参数中拉出元素,直到找到非< 24
元素。 2
和3
都通过了该测试,因此takeWhile (< 24)
确实需要知道列表理解的第三个元素是什么。
但是takeWhile
并不是真正的问题; 问题是您写了一个列表理解来查找所有主要因素(然后什么也没有),然后尝试对结果进行过滤以切断对所有可能的更高质数的无限探索。可能是因素。 如果您停止考虑,那真的没有任何意义。 根据定义,不是主要因子的任何事物都不能成为该列表的元素,因此您不能从该列表中滤除大于n
的非因子。 取而代之的是,您需要过滤对列表理解的输入 ,以便它不会尝试探索无限空间,如@nm的答案所示。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.