6

我在 Stack Overflow 上查看了许多问题的答案,试图找到解决我在使用 Reactive Banana 库时遇到的问题的方法。所有的答案都使用了一些我不太理解的“mapAccum”魔法。查看 API 文档,我发现只有“ accumEand的有效组合accumB”。这不是很有帮助。

似乎这个函数可以用来比较Behavior两个连续事件时 a 的值,这是我想做的。但我不清楚如何使这项工作。

具体是如何mapAccum工作的?

4

3 回答 3

4

mapAccum函数与标准 Data.List 模块中的函数非常相似mapAccumL,因此得名。Hackage 的文档还包括一个指向mapAccum. 连同类型签名,希望这足以说明这个函数是如何工作的。

再说一次,我可以改进文档。:-) 但是除了粘贴源代码之外,我还不太清楚如何做到这一点。结果的第二部分很容易用以下等式描述

snd . mapAccum acc = accumB acc . fmap (. snd)

但是第一部分没有这么好的等式。

我可以用文字来描述:

该函数通过应用 type 的第二个参数中包含的函数来mapAccum累积 type 的状态。该函数返回一个事件,其发生是值和跟踪累积状态的行为。换句话说,这是一台 Mealy 机器或状态自动机。accEvent t (acc -> (x,acc))xacc

但我不确定这些话是否真的有帮助。

于 2012-09-08T11:25:49.947 回答
4

请注意

mapAccum :: acc -> Event t (acc -> (x, acc)) -> (Event t x, Behavior t acc)

所以它需要一个初始值:: acc来累积,以及一个事件,它产生一个函数,在产生输出值的同时更新累积值::x。(通常你会通过部分应用一些函数来创建这样的事件<$>。)结果你会得到一个新的事件,它会在你的x值出现时触发你的值,以及一个包含你当前累积值的行为。

如果mapAccum您有一个事件并且您想要做出相关的行为和事件,请使用它。

例如,在您的其他问题的问题域中,假设您有一个eTime :: Event t Int异常触发的事件,并且您想计算eDeltaTime :: Event t Int差异和bTimeAgain :: Behaviour t Int 当前使用的时间:

type Time = Int
type DeltaTime = Time 

getDelta :: Time -> Time -> (DeltaTime,Time)
getDelta new old = (new-old,new)

我本可以这样写getDelta new = \old -> (new-old,new)以使下一步更清晰:

deltaMaker :: Event t (Time -> (DeltaTime,Time))
deltaMaker = getDelta <$> eTime

(eDeltaT,bTimeAgain) = mapAccum 0 $ deltaMaker

在这种情况下,bTimeAgain将是与 中的事件具有相同值的行为eTime。发生这种情况是因为我的getDelta函数new直接通过未更改的 fromeTimeacc值。(如果我bTimeAgain自己想要,我会使用stepper :: a -> Event t a -> Behaviour t a。)如果我不需要bTimeAgain,我可以只写(eDeltaT,_) = mapAccum 0 $ deltaMaker

于 2012-09-08T17:13:17.723 回答
1

注意:我正在使用答案,所以我可以编写格式化代码

这是我记录该功能的尝试:


mapAccum ::                  acc -- Initial Accumulation Value
  ->   Event t (acc -> (x, acc)) -- Event stream that of functions that use the previous
                                 -- accumulation value to:
                                 -- a) produce a new resulting event (x) and,
                                 -- b) updates the accumulated value (acc)
  -> (Event t x, Behavior t acc) -- fst) a stream of events from of a) above
                                 -- snd) a behavior holding the accumulated values b) above

该功能mapAccumLData.List模块中的功能类似。它是 和 的有效accumE组合accumB。结果Behavior对于保存可能需要计算事件流的值 (x) 的先前事件的历史记录非常有用。

示例:计算最后 5 个事件的滚动平均值

rolingAverage :: forall t. Frameworks t => Event t Double -> Event t Double
rolingAverage inputStream = outputStream
  where
    funct x xs = (sum role / 5, role) where role = (x:init xs)
    functEvent = funct <$> inputStream -- NB: curries the first parameter in funct
    (outputStream,_) = mapAccum [0,0,0,0,0] functEvent  
于 2012-09-10T02:27:26.367 回答