6

你如何处理反应香蕉中的当前时间?

理想情况下,我想要一个Behaviour我可以“轮询”以获取当前时间的东西。Behaviour但是,用s轮询s Event(通过<@等)给了我Behaviour来自previous Event的值,而不是当前值。(我意识到这是为了避免确实有用的循环定义。)

我发现fromPoll我认为会有所帮助。 Behaviour观察到的 sfromPoll不能依赖于它们自己,因此不能通过观察在this被触发之前Event而不是在前一次触发之后Event的行为来引入循环。

题外话

用更正式的术语来说,我建议Events 总是在时间t+发生,并且Behaviours总是在时间t-被观察到,即Events 观察在它们之前无限短时间发生的行为。Behaviour由和朋友生成的 s 的新值accumB总是从时间t+开始,因此无法被Events 观察到,这也发生在时间t+

在这个提议的语义下Behaviour,由创建的语义fromPoll将在每个处理之前更新Event。其他Behaviours 将在之后更新,因为它们是由accumB和朋友创建的。

我的用例

无论如何,这对我的主要问题来说是一个重要的题外话。我想知道是否有某种方法可以在反应香蕉中处理当前时间(而不是上一个时间) 。 Event例如,我的用例是跟踪实体发送的 ping,以及它们中的任何一个是否在特定时间间隔内未发送 ping 以发出警告事件的信号。

当然,我可以并且会非常频繁地触发事件,所以我的警告不会有很大的错误。然而,它们似乎并不精确。

处理这个问题的正确方法是什么?

4

1 回答 1

2

鉴于您的示例用例,我认为如果您远离fromPoll. 为了解释原因,需要做一些澄清。(注意:在下文中,“流”指的是一个Event t a,而“发生”指的是组成它们的触发之一。)

但是,使用事件轮询行为(通过<@等)为我提供了前一个事件的行为值,而不是当前值。

我想您是在暗指以下文档中的解释,例如stepper

请注意,比较中的小于号timex < time表示行为的值在事件发生“稍微之后”发生变化。这允许递归定义。

但是,该延迟仅与用于定义行为的流(即您传递给stepper/accumB的流)以及与之同步的任何流有关。例如,假设您有两个独立的流eTickeTock,以及以下网络片段:

eIncrement = (+1) <$ eTick
bCount = accumB 0 eIncrement
eCountTick = bCount <@ eTick

eCountTock = bCount <@ eTock

eIncrement并且与eCountTick同步eTick,因此观察到的值eCountTick是“旧”值;即同步更新前的值。然而,从 给出的观点来看eCountTock,这些都不重要。对于使用 的观察者eCountTock来说,没有延迟可言,并且值始终是当前值。

从中观察到的行为fromPoll不能依赖于它们自己,因此不能通过在触发此事件之前而不是仅在前一个事件触发之后观察行为来引入循环。

我们只关心与更新行为的流同步。因此,就观察值而言,“就在下一次发生之前”和“就在上一次发生之后”归结为同一件事。fromPoll,但是,这使事情变得有些混乱。它创建了一个行为,每当事件网络中发生任何事件时都会更新该行为;因此更新与所有流的联合同步。没有独立于fromPoll事件的流这样的东西,因此观察到的值将受到延迟的影响,但我们观察它。那样的话,fromPoll对于应用程序驱动时钟来说是行不通的,因为它需要以一定的精度跟踪连续变化。

以上所有内容都暗示反应香蕉没有内置的时间概念。每个流中只有“逻辑”时间线,可以通过合并流来交织。因此,如果我们想要一个当前时间行为,我们最好的选择是从一个独立的流中构建一个。这是该方法的演示,只要精度threadDelay允许,它将产生新鲜和及时的结果:

{-# LANGUAGE RankNTypes #-}
module Main where

import Control.Concurrent
import Control.Monad
import Data.Time
import Reactive.Banana
import Reactive.Banana.Frameworks

main = do
    let netDesc :: forall t. Frameworks t => Moment t ()
        netDesc = do
            (eTime, fireTime) <- newEvent
            liftIO . forkIO . forever $
                threadDelay (50 * 1000) >> getCurrentTime >>= fireTime
            bTime <- flip stepper eTime <$> liftIO getCurrentTime
            (eTick, fireTick) <- newEvent
            liftIO . forkIO . forever $
                threadDelay (5000 * 1000) >> fireTick ()
            reactimate $ print <$> bTime <@ eTick
    network <- compile netDesc
    actuate network >> threadDelay (52000 * 1000) >> pause network

bTimeeTime每0.05s更新一次;它是通过 观察到eTick的,一个独立的流eTime,每 5 秒出现一次。然后,您可以使用eTick从它派生的流来观察和更新您的实体。或者,您可以结合bTime应用样式中的实体行为来获取,例如,最新 ping 的行为,以使用eTick.

在您的情况下,具有规范的时间行为看起来像是一种合理的方法;它在概念上是清晰的,并且很容易概括为多个刻度。在任何情况下,您可以使用的其他方法包括摆脱bTimeeTick用作低分辨率的当前时间流(尽管这似乎会使threadDelay错误建立得更快),以及摆脱eTick使用changes来获得新鲜的流从行为中更新值(通过它带有自己的怪癖和烦恼,正如文档所暗示的那样)。

于 2014-06-12T09:49:25.903 回答