2

我听说在 Haskell 中,我们可以使用它MonadFix来访问将来要评估的值。但我认为Monads 只是语法糖,所以应该有类似的东西可以在纯函数中实现。所以我尝试了以下方法:

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)]

我做错了什么吗?

4

1 回答 1

9

跳过关于 的部分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不是你想要的。

现在,为什么?好吧,您使用的foldr结果类似于:

app 'd' (app 'd' (app 'f' ... (app 'f' (0,b)))))))))

所以app正在做的计数是从列表的右边开始的。如果我们改为将其更改为foldl从另一侧处理列表的 a,并更改其余部分代码以与其对齐:

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)]
于 2013-09-24T07:17:05.710 回答