繁体   English   中英

Haskell不会懒惰地评估

[英]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]

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

举例说明发生了什么:

http://sketchtoy.com/67338195

您的问题不是直接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的非因子。 取而代之的是,您需要过滤对列表理解的输入 ,以便它不会尝试探索无限空间,如@nm的答案所示。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM