Как оглянуться назад на предыдущий момент
Я читаю состояние кнопки (независимо от того, нажата она или нет) каждый момент:
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
, чтобы узнать, является ли кнопка новой нажал или нет в данный момент.
Возможно ли это вообще? Как бы вы это сделали? Я был бы признателен, если бы существовала лучшая или более распространенная идея или метод для достижения этой цели.
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
.- для точного определения времени событий и обновления поведения в реактивном банане см.этот вопрос .
- в зависимости от того, что вы пытаетесь сделать,
changes
функция может быть полезной. Имейте в виду предостережения, связанные с ним, упомянутые в документации.