我已经在Stack Overflow上查看了一些问题的答案,试图在使用Reactive Banana库时找到问题的解决方案。所有答案都使用了一些 magic ,使用了'mapAccum',这是我无法理解的。查看API文档,我发现“accumE
和accumB
的高效组合”。这不是很有帮助。
似乎这个函数可以用来比较两个连续事件时Behavior
的值,这是我想要做的。但我不清楚如何做到这一点。
mapAccum
究竟是如何运作的?
答案 0 :(得分:4)
mapAccum
函数与标准Data.List模块中的mapAccumL
函数非常相似,因此名称。 Hackage的文档还包含source code of mapAccum
的链接。与type signature一起,希望有足够的信息来确定这个函数的工作原理。
然后,我可以改进文档。 :-)但我还不清楚如何做到这一点,不能粘贴源代码。结果的第二部分易于通过以下等式描述
snd . mapAccum acc = accumB acc . fmap (. snd)
但第一部分没有这么好的等式。
我可以用文字写一个描述:
函数
mapAccum
通过应用类型acc
的第二个参数中包含的函数来累积类型Event t (acc -> (x,acc))
的状态。该函数返回一个事件,其出现值为x
,并返回跟踪累积状态acc
的行为。换句话说,这是一台Mealy机器或州自动机。
但我不确定这些词是否真的有帮助。
答案 1 :(得分:4)
请注意
mapAccum :: acc -> Event t (acc -> (x, acc)) -> (Event t x, Behavior t acc)
所以它需要一个初始值:: acc
来累积,一个事件产生一个函数
在产生输出值::x
时更新累计值。 (通常,您通过<$>
部分应用某些功能来制作此类事件。)
因此,您会收到一个新事件,只要它们出现并且包含行为,就会触发x
值
你目前的累积价值。
如果您有某个活动并且想要制作相关的行为和活动,请使用mapAccum
。
例如,在your other question的问题域中,假设您有一个不正常的事件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
从eTime
传递到acc
值。
(如果我自己想要bTimeAgain
,我会使用stepper :: a -> Event t a -> Behaviour t a
。)
如果我不需要bTimeAgain
,我可以写(eDeltaT,_) = mapAccum 0 $ deltaMaker
。
答案 2 :(得分: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
此函数类似于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