如何使“时间机器”在Haskell中工作?
我听说在Haskell中,我们可以使用MonadFix
来访问将在未来评估的值。但我认为Monad
只是语法糖,所以应该有类似的东西,可以在纯函数中实现。所以,我试过如下:如何使“时间机器”在Haskell中工作?
timemachine :: [a] -> (a -> Int -> b -> b) -> b -> b
timemachine al f b = result where
~(total, result) = foldr app (0,b) al
app a (i,b1) = (i+1, f a (total - i) b1)
main :: IO()
main = print $ timemachine "ddfdfeef" (\x i y -> (x,i):y) []
但预计不会输出:
[('d',1),('d',2),('f',3),('d',4),('f',5),('e',6),('e',7),('f',8)]
理想的结果应该是
[('d',8),('d',7),('f',6),('d',5),('f',4),('e',3),('e',2),('f',1)]
难道我做错了什么?
跳过大约MonadFix
的部分,似乎要使用一个值,该值至少部分地需要本身要构造(total
是基于这又是基于total
折叠)。
你实际上已经这么做了,只有你的期望不符合你的折叠!要查看你可以改变timemachine
到
timemachine al f b = result where
~(total, result) = foldr app (0,b) al
app a (i,b1) = (i+1, f a i b1)
这将产生
[('d',7),('d',6),('f',5),('d',4),('f',3),('e',2),('e',1),('f',0)]
所以total-i
工作的问题,它只是你的积累i
S中的不是你想要的。
现在,为什么?那么,你使用foldr
该工程以类似:
app 'd' (app 'd' (app 'f' ... (app 'f' (0,b)))))))))
所以计数是app
正在做的是被从列表右侧的工作。如果我们不是将其更改为foldl
从对方的作品名单,并改变一些代码的其余部分的排队吧:
timemachine :: [a] -> (a -> Int -> b -> b) -> b -> b
timemachine al f b = result where
~(total, result) = foldl app (0,b) al
app (i,b1) a = (i+1, f a (total-i) b1)
main :: IO()
main = print $ timemachine "ddfdfeef" (\x i y -> y++[(x,i)]) []
你得到你想要的东西:
[('d',8),('d',7),('f',6),('d',5),('f',4),('e',3),('e',2),('f',1)]
很好的答案,你解决了这个问题! –
我应该用'foldl''来代替吗?我听说'foldl'是要避免的。 –
通常使用'foldl''比'foldl'更受欢迎,因为它的累加器是严格的,当你向左折叠时,你通常对懒惰不感兴趣。然而,在这种情况下,有两个问题: 1.我们的累加器是一对,'seq'(由'fold'使用)只评估HNF,'app'已经这样做,所以没有严格性增益那里。 2.我们对懒惰感兴趣,这就是为什么这项工作。如果我们用'deepseq'这样的东西来折叠,它会尽早评估结果列表,而且它不会完成。 – ollanta
你为什么期待这样的结果?懒惰(?)来自你的代码? –
那么我的实现可能完全错误,但有没有什么正确的方法来获得预期的结果?关键是我需要访问未来的价值 - 在上面的例子中,列表的长度。 –
如果我正确理解你,你需要使用'MonadFix'来请求一些代码的解除版本。你给了我们最初的东西似乎是公平的。 :-) –