【问题标题】:reactive-banana throttling events反应性香蕉节流事件
【发布时间】:2012-06-08 23:17:00
【问题描述】:

我想在响应式香蕉中实现某种类型的事件限制。它应该这样工作,如果从最后一个通过的事件到达的时间少于 delta 秒,则不会让事件通过。如果它没有通过,那么它会被存储并在最后一次触发事件的 delta 秒后触发。

下面是一个为时间戳数字列表实现此功能的程序。有没有可能把它翻译成反应香蕉?

另外,在响应式香蕉中,我如何在其他事件进入 x 秒后触发一个事件?

模块主要在哪里 导入数据列表 -- 1 秒节流 -- 逻辑是在输出最后一个值后 1 秒之前永远不会输出一个值。 主要 :: IO() 主要 = 打印 $ 测试 [ (0.0, 1.0), (1.1, 2.0), (1.5,3.0), (1.7,4.0), (2.2, 5.0) ] --应该输出 [ (0.0, 1.0), (1.1, 2.0), (2.1,4.0), (3.1, 5.0) ] 测试 :: [(Double,Double)] -> [(Double,Double)] 测试列表 = g v (concat xs) 在哪里 (v, xs) = mapAccumL f (-50,Nothing) 列表 g (t, Just x) ys = ys ++ [ (t+1,x) ] g _ ys = ys f (lasttime, Just holdvalue) (t,x) = if t > (lasttime+1) then 如果 t > (lasttime + 2) 那么 ( (t, Nothing), [ (lasttime+1,holdvalue), (t,x)] ) else ( (lasttime+1, Just x) , [ (lasttime+1,holdvalue) ] ) 别的 ((上次,只是 x),[]) f (lasttime, Nothing) (t,x) = 如果 t > (lasttime+1) 那么 ( (t,Nothing) , [ (t, x ) ] ) else ( (lasttime, Just x), [] )

【问题讨论】:

    标签: haskell reactive-programming frp


    【解决方案1】:

    好的,我设法实现了我在问题中描述的内容。我不太高兴需要 IO 来通过反应控制计时器。我想知道是否可以使用带有签名的节流阀 throttle::Event t a -> Int -> Event t a ...

    ps:我是 Haskell 的新手,所以代码可能会更紧凑或更优雅。

    {-----------------------------------------------------------------------------
    
    ------------------------------------------------------------------------------}
    {-# LANGUAGE ScopedTypeVariables #-} -- allows "forall t. NetworkDescription t"
    
    import Graphics.UI.WX hiding (Event)
    import Reactive.Banana
    import Reactive.Banana.WX
    import Data.Time
    
    {-----------------------------------------------------------------------------
        Main
    ------------------------------------------------------------------------------}
    
    data ThrottledValue a = FireStoredValue a | FireNowAndStartTimer a| HoldIt a | Stopped deriving Show
    data ThrottledEvent a = TimerEvent | RealEvent a deriving Show
    
    main = start $ do
        f   <- frame [text := "Countercesss"]
        sl1  <- hslider f False 0 100 []
        sl2  <- hslider f False 0 100 []
        set f [ layout := column 0 [widget sl1, widget sl2] ]
        t <- timer f []
        set t [ enabled := False ] 
        let networkDescription :: forall t. NetworkDescription t ()
            networkDescription = do
            slEv <- event0 sl1 command
            tick <- event0 t command 
            slB <- behavior sl1 selection
            let (throttledEv, reactimates) = throttle (slB <@ slEv) tick t 100
            reactimates
            reactimate $ fmap (\x ->  set sl2 [selection := x]) throttledEv       
        net <- compile networkDescription
        actuate net            
    
    throttle::Event t a -> Event t () -> Timer -> Int -> (Event t a, NetworkDescription t () )    
    throttle ev tick timer dt = (throttledEv, reactimates)
            where   
                    all = union (fmap (\x-> RealEvent x) ev) (fmap (\x -> TimerEvent) tick)
                    result = accumE Stopped $ fmap h all
                            where
                            h (RealEvent x) Stopped = FireNowAndStartTimer x
                            h TimerEvent Stopped = Stopped
                            h (RealEvent x) (FireNowAndStartTimer _) = HoldIt x
                            h TimerEvent (FireNowAndStartTimer _) = Stopped
                            h (RealEvent x) (HoldIt _) = HoldIt x
                            h (TimerEvent) (HoldIt y) = FireStoredValue y
                            h (RealEvent x) (FireStoredValue _) = HoldIt x
                            h (TimerEvent) (FireStoredValue _) = Stopped          
                    start (FireStoredValue a) = Just $ resetTimer timer dt
                    start (FireNowAndStartTimer a) = Just $ resetTimer timer dt
                    start _ = Nothing  
                    stop Stopped = Just $ stopTimer timer
                    stop _ = Nothing  
                    reactimates = do
                            reactimate $ filterJust $ fmap stop result   
                            reactimate $ filterJust $ fmap start result
                    filterFired (FireStoredValue a) = Just a
                    filterFired (FireNowAndStartTimer a) = Just a
                    filterFired _ = Nothing
                    throttledEv = filterJust $ fmap filterFired result                 
    
    startTimer t dt = set t [ enabled := True, interval := dt ]
    stopTimer t = set t [ enabled := False ]
    resetTimer t dt = stopTimer t >> startTimer t dt
    

    【讨论】:

    • 如果您对 IO 不满意,您可以将计时器实现为侦听包含消息(“start”、“stop”、“reset”)的事件并返回另一个事件的东西。一般来说,我建议将使用reactimate 的函数放入NetworkDescription monad,即throttle :: ... -&gt; NetworkDescription t (Event t a) 而不是throttle :: .. -&gt; (Event t a, NetworkDescription t ())
    • 好的,这确实使它更干净,并且在语法方面与使用 throttle::Event ta -> Int -> Event ta 没有太大区别(只需在 do 中使用 gist.github.com/2905841。我想我对这个解决方案很满意。
    【解决方案2】:

    从 reactive-banana-0.6 开始,绝对有可能实现您想要的功能,但它有点复杂。

    基本上,您已经使用像 wxHaskell 这样的外部框架来创建一个计时器,然后您可以使用它来安排事件。 Wave.hs 示例演示了如何做到这一点。

    目前,我选择不在响应式香蕉库本身中包含时间概念。原因很简单,不同的外部框架具有不同分辨率或质量的计时器,没有一种尺寸适合所有情况。

    我确实打算向库本身添加处理时间和计时器的通用帮助函数,但我仍然需要找到一种好方法使它在不同的计时器上通用,并弄清楚我可以提供哪些保证。

    【讨论】:

      猜你喜欢
      • 2014-06-23
      • 2013-10-19
      • 2017-11-22
      • 2013-06-20
      • 2013-11-09
      • 2011-09-25
      • 1970-01-01
      • 2012-04-17
      • 2012-10-15
      相关资源
      最近更新 更多