如何实现递归依赖于自己的反应性香蕉行为? [英] How to implement reactive-banana behaviors that recursively depend on themself?

查看:176
本文介绍了如何实现递归依赖于自己的反应性香蕉行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个行为,我想根据事件的发生和行为的当前值来改变它的值。在下面的例子中,我有两个计数器根据布尔行为是True还是False来更新。因为这段代码崩溃时发生了一个<< loop>> 异常,但我不确定如何重构它以工作或解决该问题。

I have a Behavior whose value I want to change based on the occurrence of an Event and the Behavior's current value. In the example below I have two counters that are updated based on whether a boolean behaviour is True or False. As it is this code crashes with a <<loop>> exception, but I'm unsure how to restructure it to work or how else to approach this problem.

{-# LANGUAGE ScopedTypeVariables #-}

import Reactive.Banana
import Reactive.Banana.Frameworks

import Control.Arrow
import Control.Concurrent
import Control.Monad
import Control.Monad.Fix

counter :: Bool -> Event t Int -> Behavior t Bool -> (Behavior t Int, Event t (Bool -> Bool))
counter b input active = (result, whenE ((b/=) <$> active) (fmap (const not) input))
    where result = accumB 0 (fmap (+) evt')
          evt' = whenE ((b==) <$> active) input

alternater :: Event t Int -> Behavior t Bool -> (Behavior t (Bool, (Int, Int)), Event t (Bool -> Bool))
alternater input active = ((,) <$> active <*> ((,) <$> fst t1 <*> fst t2), snd t1 `union` snd t2)
    where t1 = counter True input active
          t2 = counter False input active

main :: IO ()
main = do
    (inputHandler, fireInput) <- newAddHandler
    let network :: forall t . Frameworks t => Moment t ()
        network = do
            eInput <- fromAddHandler inputHandler
            let ui :: Behavior t (Bool, (Int, Int)) -> Moment t (Behavior t (Bool, (Int, Int)))
                ui b = do
                    let (behavior, evt) = alternater eInput (fst <$> b)
                    return $ stepper id (fmap (***id) evt) <*> behavior
            output <- changes =<< mfix ui
            reactimate $ putStrLn . show <$> output
    forkIO $ actuate =<< compile network
    forever $ getLine >>= fireInput . read


推荐答案

异常是正确的,您正在定义行为直接就其本身而言。

The exception is correct, you are defining a behavior directly in terms of itself.

alternater .. active = (.. <$> active <*> .. , ..)
ui b = do
    let (behavior, ..) = alternater .. (.. <$> b)
    return $ .. <*> behavior

... mfix ui

此代码表示当前值在 ui 中的结果行为将以循环方式依赖于自身。

This code means that the current value of the result behavior in ui would depend on itself in a circular way.

递归总是需要一点点延迟定义明确。最简便的方法是在事件和使用 stepper accumB 构建的行为之间使用相互递归。另请参阅此答案

Recursion always needs a little delay to be well-defined. The most convenient way to do that is to use mutual recursion between an event and a behavior built with stepper or accumB. See also this answer.

这篇关于如何实现递归依赖于自己的反应性香蕉行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆