$!
也没有帮助...尽管测试是在使用ghc-6.8.2的Ideone.com上进行的)。基本上,这是关于嵌套循环的问题,在列表范例中可以表述为
prod (sum,concat) . unzip $
[ (c, [r | t]) | k<-[0..kmax], j<-[0..jmax], let (c,r,t)=...]
prod (f,g) x = (f.fst $ x, g.snd $ x)
或者用伪代码表示:
let list_store = [] in
for k from 0 to kmax
for j from 0 to jmax
if test(k,j)
list_store += [entry(k,j)]
count += local_count(k,j)
result = (count, list_store)
在我添加了bang-pattern之前,我要么遇到内存溢出,要么甚至遇到堆栈溢出。但是bang pattern不是标准的一部分,对吧?所以问题是,如何用标准的Haskell编写上述代码以在恒定空间中运行?
这里是测试代码。计算是虚假的,但问题是相同的。编辑:
foldr
形式的代码是:testR m n = foldr f (0,[])
[ (c, [(i,j) | (i+j) == d ])
| i<- [0..m], j<-[0..n],
let c = if (rem j 3) == 0 then 2 else 1 ]
where d = m + n - 3
f (!c1, []) (!c, h) = (c1+c,h)
f (!c1, (x:_)) (!c, h) = (c1+c,x:h)
试图运行print $ testR 1000 1000
会导致堆栈溢出。 如果在f
中使用bang-patterns,则更改为foldl
仅成功,但它以相反的顺序构建列表。 我想懒惰地按正确的顺序构建它。 是否可以使用任何类型的fold
进行操作,以获得惯用解决方案?
f ... c ... = case (seq c False) of {True -> undefined; _ -> ...}
。教训是,只有模式匹配强制一个值,而seq
本身并不强制任何东西,而是安排当seq x y
被强制执行时-通过模式匹配-x
也将被强制执行,并且y
将是答案。与我从在线报告中理解的相反,$!
本身不会强制执行任何内容,尽管它被称为"严格应用运算符"。
@stephentetley的观点很重要 - 严格性在控制空间行为方面非常重要。因此,使用严格性注释和感叹号模式来编码Haskell中的循环是完全可以的,必要时可以编写任何所需的特殊折叠(即结构消耗)函数 - 就像我最初做的那样 - 并依赖于GHC来优化代码。
非常感谢大家的帮助。