在Haskell中总结无限列表的一部分

时间:2017-11-14 06:32:45

标签: haskell functional-programming

我对以下示例中Haskell执行的确切方式感到有些困惑

vacation = ['Rick', 'Paris', 'Tom', 'James']
print("I live with %d people" % len(vacation))

这里是用于查找小于sum (takeWhile (<10000) (filter odd (map (^2) [1..]))) 的所有奇数平方的Sum的代码,我知道这些10000takewhilefilter函数的工作原理。我的疑问是,这里map函数从无限列表中取一个元素并将其平方并将平方元素列表返回到map函数对吗?在这种情况下,它会无限地运行,以便无限的元素列表不是吗?或者只是将一个元素置于平方然后返回filter

3 个答案:

答案 0 :(得分:7)

Haskell尽可能地懒惰。它不计算你没有要求的任何值,如果你let nums = filter odd (map (^2) [1..]),你还没有强迫它计算任何东西。现在它知道nums属于Num a => [a]类型(因为你描述的操作类型)但是它不知道其他任何事情(这很好!)

即使您运行takeWhile (<10000),也不会强制使用任何数字。现在ghc知道takeWhile (<10000) nums有类型(Ord a, Num a) => [a](你在(<)比较时引入了额外的类型类),但这就是它所知道的。

即使一旦拨打sum,ghc也无需执行任何操作。 sum需要一个Num a => [a](技术上是(Num a, Foldable t) => t a,但我们现在假装它们是同一个东西)并且你给它一个。在你真正要求该操作的结果之前,ghc不会做任何。您可以在解释器中执行let foobar = sum [1..]来测试此操作。只要你没有要求foobar的结果,Haskell就可以使用那个表达式了。

但是,如果您曾要求获得该结果,那么您已迫使整条线路进行计算。 sum需要其列表,因此它会takeWhile (<10000)询问它。 takeWhile需要其列表,因此它会filter odd询问它。 filter需要其列表,因此它会map (^2)询问它,但filter一次不需要整个列表,因此map仍然可以尽可能懒散地工作并给出每次都是第一名。

一旦takeWhile找到一个数字(>=10000),它就不再需要了,并且很高兴地交给sum,这会产生你的结果并且ghc会变回懒惰,而不会产生nums的任何结果,除非您需要它们。

答案 1 :(得分:3)

您可以想象功能之间的对话。

GHCi要求sum提供结果。 sum醒来并要求takeWhile至少提供一个元素。唤醒takeWhile并要求filter至少提供一个元素。唤醒filter并要求map至少提供一个元素。唤醒map并要求[1..]至少提供一个元素。

[1..]给出1map适用(^2)并提供(1^2)filter检查odd (1^2)并提供1takeWhile检查(1&lt; 10000)并给出1sum1添加到累加器,并继续要求takeWhile至少提供一个元素。依此类推,直到takeWhile检查成功为止。然后sum将累加器返回给GHCi。

答案 2 :(得分:2)

takeWhile (<10000)限制了总和处理的元素数量。 Haskell是懒惰评估的,只会根据需要做很多工作。因此,创建无限列表并使用take操作限制它是一种常见模式。这意味着只生成和处理列表中使用的元素。一旦takeWhile条件为假,则不会使用列表中的其他元素,因此不会生成它们。