我一直在问一些关于严格性的问题,但我想我之前已经错过了这个标记。希望这更精确。
让我们说:
n = 1000000
f z = foldl' (\(x1, x2) y -> (x1 + y, y - x2)) z [1..n]
不改变f
,我应该设置什么
z = ...
这样f z
不会溢出堆栈? (即无论n的大小如何,都在恒定的空间内运行)
如果答案需要GHC扩展,那也没关系。
我的第一个想法是定义:
g (a1, a2) = (!a1, !a2)
然后
z = g (0, 0)
但我不认为g
是有效的Haskell。
答案 0 :(得分:9)
所以你的严格foldl'
只会在折叠的每一步评估你的lambda的结果 Weak Head Normal Form ,即它只在最外面的构造函数中是严格的。因此,元组将被评估,但是元组中内部元素的添加可能会形成为thunk。这个in-depth answer实际上似乎可以解决您的确切情况。
g
:您正在考虑BangPatterns
扩展,看起来像是
g (!a1, !a2) = (a1, a2)
并且在将它们返回元组之前评估a1和a2到WHNF。
你想要关注的不是你的初始累加器,而是你的lambda表达式。这将是一个很好的解决方案:
f z = foldl' (\(!x1, !x2) y -> (x1 + y, y - x2)) z [1..n]
编辑:在注意到您的其他问题后,我发现我没有仔细阅读过这个问题。你的目标是拥有“严格的数据”。那么,您的另一个选择是创建一个在其字段上具有严格标记的新元组类型:
data Tuple a b = Tuple !a !b
然后,当您在Tuple a b
上进行模式匹配时,将评估a
和b
。
无论如何,您都需要更改功能。
答案 1 :(得分:3)
如果不改变f
,你无能为力。如果f
在对的类型中被重载,则可以使用严格对,但是当它处于f
时,您将被锁定。编译器(严格性分析和转换)可以避免堆栈增长,这是一个小小的希望,但是你无法指望它。