不确定如何获得正确的评估订单

时间:2012-10-24 16:47:20

标签: haskell ghci operator-precedence strictness

我不确定这两段代码之间的区别是什么(相对于x),但第一段代码完成了:

$ foldr (\x y -> if x == 4 then x else x + y) 0 [1,2 .. ]
10

而第二个没有(至少在GHCi中):

$ foldr (\x (y, n) -> if x == 4 then (x, n) else (x + y, n + 1)) (0, 0) [1,2 .. ]
.......

我做错了什么阻止了第二个例子在点击x == 4时完成,就像第一个例子一样?

我尝试在xx == 4let内)添加爆炸模式,但似乎没有任何区别。

3 个答案:

答案 0 :(得分:4)

你的第二个例子有两个相关的问题。

回想一下,foldr产生了一个右关联嵌套,即foldr f z [a, b, c] = f a (f b (f c z))。因此,您给出的函数的第二个参数foldr表示折叠整个剩余列表的 final 值。在无限列表的情况下,只有在生成另一个惰性无限数据结构时,或者在某些时候,完全忽略第二个参数,这是第一个示例所做的。

您的第二个示例始终使用输入元组中的第二个项来计算结果元组的第二项,因此当应用于无限列表时,您最终会得到无限回归。将0指定为“起始”值没有帮助,因为在foldr中,起始值位于列表的末尾,无限列表显然没有 - 似乎你想要不变地传递值,但这需要使用!

开始(或者可能“结束”)一个值

这只会导致结果中的第二项无法终止。结果的第一项定义明确,或至少可以。这里的问题是模式匹配强制评估,这可以防止在整个折叠完成之前产生任何结果。这可以通过多种方式避免,但只需使用fstsnd就足够了:\x yn -> if x == 4 then (x, snd yn) else (x + fst yn, snd yn + 1)应该为您提供一个由元组组成的结果,其第一项是您所期望的(但是第二项是未定义的,如上所述。

答案 1 :(得分:3)

第一个没有使用"累加器"的值。 (即y),而第二个使用n。计算n需要折叠整个列表,这就是它永远不会终止的原因。没有多少严格性可以解决这个问题。

事实上,您的算法并没有按照您的想法执行。您可能会混淆foldrfoldl的行为。即使你能以某种方式计算n,它的值也是无限的。考虑foldr的最佳方法是用第一个函数替换列表中的每个(:),并用第二个值替换终端[]

答案 2 :(得分:3)

这是模式匹配/评估发生的方式。如果您按照f = \x (y, n) -> ...定义它,那么

foldr f (0, 0) [1,2 .. ] =
   f 1 (foldr f (0,0) [2..]) =
   (\x (y, n) -> ...) 1 (foldr f (0,0) [2..])

并且(y,n)模式是严格匹配的,所以要计算任何东西,需要计算第二个参数(foldr .. [2..]),然后需要foldr .. [3..],依此类推。

可以使用~(称为“无可辩驳的模式”)使模式匹配非严格,这会稍微改变行为。

> foldr (\x ~(y,n) -> if x == 4 then (x,n) else (x+y,n+1)) (0,0) [1..]
(10,

(因为refutable版本甚至无法计算元组的第一个元素。)