我在 Stack Overflow 上查看了许多问题的答案,试图找到解决我在使用 Reactive Banana 库时遇到的问题的方法。所有的答案都使用了一些我不太理解的“mapAccum”魔法。查看 API 文档,我发现只有“ accumE
and的有效组合accumB
”。这不是很有帮助。
似乎这个函数可以用来比较Behavior
两个连续事件时 a 的值,这是我想做的。但我不清楚如何使这项工作。
具体是如何mapAccum
工作的?
我在 Stack Overflow 上查看了许多问题的答案,试图找到解决我在使用 Reactive Banana 库时遇到的问题的方法。所有的答案都使用了一些我不太理解的“mapAccum”魔法。查看 API 文档,我发现只有“ accumE
and的有效组合accumB
”。这不是很有帮助。
似乎这个函数可以用来比较Behavior
两个连续事件时 a 的值,这是我想做的。但我不清楚如何使这项工作。
具体是如何mapAccum
工作的?
该mapAccum
函数与标准 Data.List 模块中的函数非常相似mapAccumL
,因此得名。Hackage 的文档还包括一个指向mapAccum
. 连同类型签名,希望这足以说明这个函数是如何工作的。
再说一次,我可以改进文档。:-) 但是除了粘贴源代码之外,我还不太清楚如何做到这一点。结果的第二部分很容易用以下等式描述
snd . mapAccum acc = accumB acc . fmap (. snd)
但是第一部分没有这么好的等式。
我可以用文字来描述:
该函数通过应用 type 的第二个参数中包含的函数来
mapAccum
累积 type 的状态。该函数返回一个事件,其发生是值和跟踪累积状态的行为。换句话说,这是一台 Mealy 机器或状态自动机。acc
Event t (acc -> (x,acc))
x
acc
但我不确定这些话是否真的有帮助。
请注意
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
直接通过未更改的 fromeTime
到acc
值。(如果我bTimeAgain
自己想要,我会使用stepper :: a -> Event t a -> Behaviour t a
。)如果我不需要bTimeAgain
,我可以只写(eDeltaT,_) = mapAccum 0 $ deltaMaker
。
注意:我正在使用答案,所以我可以编写格式化代码
这是我记录该功能的尝试:
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
该功能mapAccumL
与Data.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