filter与takeWhile差异及运行时问题:GHCI执行效率差异原因探究
为什么两个Haskell表达式执行差异这么大?
这问题问得挺有意思的,我来给你拆解清楚哈!
首先得先搞懂两个表达式的核心区别,还有Haskell惰性求值的特性在这儿起的关键作用:
第一个表达式:快速返回的原因
我们看这段代码:
sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
这里的核心是takeWhile (<10000)函数——它的逻辑是:从输入列表里逐个取元素,一旦遇到第一个不满足<10000的元素,就立刻停止生成后续所有元素。
我们算一下平方数:100的平方是10000,刚好是第一个不满足<10000的数。所以前面的奇数平方数到99²=9801就截止了,整个被处理的列表是有限的,sum自然能快速算出结果(也就是你看到的166650)。
第二个表达式:永远跑不完的原因
再看这段代码:
sum (filter (<10000) (filter odd (map (^2) [1..])))
这里用的是filter (<10000),filter的逻辑和takeWhile完全不同:它会遍历整个输入列表,把所有满足条件的元素筛选出来。但问题在于,它的输入是map (^2) [1..]——这是一个无限列表(从1开始的所有整数的平方)。
虽然后面的平方数(比如100²、101²……)都远大于10000,filter会把它们全部过滤掉,但filter不知道“后面再也不会有满足条件的元素了”,它会一直不停地生成下一个平方数、检查是否是奇数、再检查是否小于10000,永远停不下来。
这里要澄清:这不是无限循环,而是在遍历一个无限列表,永远到不了头,所以你等了10秒只能手动中断。
核心差异总结
takeWhile是“遇到不符合条件的就停”,能把无限列表截断成有限列表,所以可以快速完成计算。filter是“遍历整个列表找符合条件的”,面对无限列表时会一直遍历下去,永远无法终止计算。
内容的提问来源于stack exchange,提问作者devio
相关产品推荐
相关产品推荐

