我不确定这两段代码之间的区别(相对于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
时无法完成,就像第一个示例一样?我试过在
x
和x == 4
(在let
内)都添加爆炸样式,但似乎都没有什么不同。 最佳答案
您的第二个示例有两个相关问题。
回想一下foldr
会产生一个右关联嵌套,即foldr f z [a, b, c]
= f a (f b (f c z))
。因此,给您foldr
函数的第二个参数表示折叠整个剩余列表的最终值。对于无限列表,只有当您生成另一个惰性无限数据结构,或者像第一个示例一样完全忽略第二个参数时,才有可能。
您的第二个示例始终使用输入元组中的第二项来计算结果元组的第二项,因此,当将其应用于无限列表时,您将得到无限回归。将0
指定为“起始”值无济于事,因为在foldr
中,起始值位于列表的末尾,而无限列表显然没有该起始值-似乎您想传递值保持不变,但这需要具有一个值才能开始(或可能是“结束”)!
那只会导致结果中的第二项不终止。结果的第一项是明确定义的,或者至少可以是。这里的问题是图案匹配力评估,这会阻止在整个折叠完成之前产生任何结果。可以通过多种方式避免这种情况,但是简单地使用fst
和snd
是很容易的:\x yn -> if x == 4 then (x, snd yn) else (x + fst yn, snd yn + 1)
应该为您提供一个由元组组成的结果,该元组的第一项是您期望的(但第二项是未定义的,例如以上说明)。