如何回顾前一刻
How to look back to the previous moment
我每时每刻都在读取按钮的状态(无论是否按下):
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
...
所有读取状态都存储到网络描述eButtonState
中desc
。
当当前时刻的状态为1
而前一时刻的状态为0
时,按钮被认为是新按下的。所以,如果事件序列是一个列表,函数将这样写:
f :: [Bool] -> Bool
f (True:False:_) = True
f _ = False
我想把这个功能应用到eButtonState
,这样我就可以知道按钮是否是新按下的。
有可能吗?你会怎么做?如果有更好或更通用的想法或方法来实现这个目标,我将不胜感激。
这是一种方法(这是一个可运行的演示):
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
。
- 有关 reactive-banana 中事件时间和行为更新的细则,请参阅 this question。
- 根据您要执行的操作,
changes
函数可能会有用。请注意文档中提到的与之相关的注意事项。
我每时每刻都在读取按钮的状态(无论是否按下):
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
...
所有读取状态都存储到网络描述eButtonState
中desc
。
当当前时刻的状态为1
而前一时刻的状态为0
时,按钮被认为是新按下的。所以,如果事件序列是一个列表,函数将这样写:
f :: [Bool] -> Bool
f (True:False:_) = True
f _ = False
我想把这个功能应用到eButtonState
,这样我就可以知道按钮是否是新按下的。
有可能吗?你会怎么做?如果有更好或更通用的想法或方法来实现这个目标,我将不胜感激。
这是一种方法(这是一个可运行的演示):
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
。 - 有关 reactive-banana 中事件时间和行为更新的细则,请参阅 this question。
- 根据您要执行的操作,
changes
函数可能会有用。请注意文档中提到的与之相关的注意事项。