我对以下示例中Haskell执行的确切方式感到有些困惑
vacation = ['Rick', 'Paris', 'Tom', 'James']
print("I live with %d people" % len(vacation))
这里是用于查找小于sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
的所有奇数平方的Sum的代码,我知道这些10000
,takewhile
,filter
函数的工作原理。我的疑问是,这里map
函数从无限列表中取一个元素并将其平方并将平方元素列表返回到map
函数对吗?在这种情况下,它会无限地运行,以便无限的元素列表不是吗?或者只是将一个元素置于平方然后返回filter
?
答案 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..]
给出1
。 map
适用(^2)
并提供(1^2)
。 filter
检查odd (1^2)
并提供1
。 takeWhile
检查(1&lt; 10000)并给出1
。 sum
将1
添加到累加器,并继续要求takeWhile
至少提供一个元素。依此类推,直到takeWhile
检查成功为止。然后sum
将累加器返回给GHCi。
答案 2 :(得分:2)
takeWhile (<10000)
限制了总和处理的元素数量。 Haskell是懒惰评估的,只会根据需要做很多工作。因此,创建无限列表并使用take操作限制它是一种常见模式。这意味着只生成和处理列表中使用的元素。一旦takeWhile
条件为假,则不会使用列表中的其他元素,因此不会生成它们。