【问题标题】:How to look back to the previous moment如何回顾前一刻
【发布时间】:2015-07-27 05:01:01
【问题描述】:

我每时每刻都在读取按钮的状态(无论是否按下):

readButton :: IO Boolean
readButton = ...

main = do
    (add, fire) <- newAddHandler
    network <- compile (desc add)
    actuate network
    forever $ do
        buttonState <- readButton
        fire buttonState

desc addButtonEvent = do
    eButtonState <- fromAddHandler addButtonEvent
    ...

所有读取状态都存储在网络描述desc中的eButtonState中。

当当前时刻的状态为1,前一时刻的状态为0时,该按钮被认为是新按下的。所以,如果事件序列是一个列表,那么函数应该是这样写的:

f :: [Bool] -> Bool
f (True:False:_) = True
f _              = False

我想将此功能应用于eButtonState,这样我就可以知道该按钮是否是新按下的。

有可能吗?你会怎么做?如果有更好或更常见的想法或方法来实现这一目标,我将不胜感激。

【问题讨论】:

    标签: haskell frp reactive-banana


    【解决方案1】:

    这是一种方法(这是一个可运行的演示):

    import Reactive.Banana
    import Reactive.Banana.Frameworks
    import Control.Monad
    import Control.Applicative -- Needed if you aren't on GHC 7.10.
    
    desc addDriver = do
        -- Refreshes the button state. Presumably fired by external IO.
        eButtonDriver <- fromAddHandler addDriver
        let -- Canonical repersentation of the button state.
            bButtonState = stepper False eButtonDriver
            -- Observes the button just before changing its state.
            ePreviousState = bButtonState <@ eButtonDriver
            -- Performs the test your f function would do.
            newlyPressed :: Bool -> Bool -> Bool
            newlyPressed previous current = not previous && current
            -- Applies the test. This works because eButtonDriver and
            -- ePreviousState are fired simultaneously.
            eNewlyPressed = unionWith newlyPressed
                ePreviousState eButtonDriver
            -- The same but more compactly, without needing ePreviousState.
            {-
            eNewlyPressed = newlyPressed <$> bButtonState <@> eButtonDriver
            -}
        reactimate (print <$> eNewlyPressed)
    
    main = do
        (addDriver, fireDriver) <- newAddHandler
        network <- compile (desc addDriver)
        actuate network
        -- Demo: enter y to turn the button on, and any other string to
        -- turn it off.
        forever $ do
            buttonState <- (== "y") <$> getLine
            fireDriver buttonState
    

    注意事项:

    • 事件是暂时的,行为是永久的是一个很好的通用规则来决定您是否需要行为或事件流。在这种情况下,您需要查看更新前的按钮状态,以确定它是否是新更新的。那么,很自然的事情就是用一个行为 (bButtonState) 来表示按钮状态,该行为由外部触发的事件 (eButtonDriver) 更新。
    • 有关组合器正在做什么的详细信息,请参阅Reactive.Banana.Combinators
    • 有关反应香蕉中事件和行为更新时间的详细信息,请参阅this question
    • 根据您要执行的操作,changes 函数可能很有用。请注意文档中提到的与之相关的注意事项。

    【讨论】:

    • 谢谢@duplode 的回答,你在哪里运行这个演示?我在 CMD 上运行它,但只有当我输入 y 时它才会打印出 True,因为 getLine 等待我的输入,并且只有 y 可以输入到 buttonState,因为 (== "y") 过滤器
    • @RyoichiroOka 只要您在输入“y”以外的内容时收到False,它就会按预期工作。这个想法是(== "y") &lt;$&gt; getLine 是您的readButton IO 操作的模型。当您键入“y”时,模型返回 True,否则返回 False
    • 我明白了。谢谢!另外我想知道bButtonState &lt;@ bButtonDriver 是如何回顾以前的状态的。这是否意味着我可以随心所欲地往后看?
    • @RyoichiroOka 这不是真正的回顾。它之所以有效,是因为stepper 执行更新(名义上)"slightly after" 触发事件,因此这样的用例可以工作。由于eButtonDriverePreviousState 是同时发生的,ePreviousState 会观察更新前的值。如果我调用它可能会更清楚,而不是ePreviousStateeCurrentState(相对于eButtonDriver带来的新状态)。
    • 我想总结一下。你认为这个函数是否正确地将给定事件流的先前值和当前值与流的给定初始值配对? \default event -&gt; (,) &lt;$&gt; stepper default event &lt;@&gt; event@duplode
    猜你喜欢
    • 1970-01-01
    • 2022-07-27
    • 2016-09-07
    • 2023-03-22
    • 1970-01-01
    • 2021-04-27
    • 2016-05-19
    • 2020-06-11
    • 1970-01-01
    相关资源
    最近更新 更多