15

次のようなコードがあります。

 guiState :: Discrete GuiState
 guiState = stepperD (GuiState []) $
   union (mkGuiState <$> changes model) evtAutoLayout

 evtAutoLayout :: Event GuiState
 evtAutoLayout = fmap fromJust . filterE isJust . fmap autoLayout $ changes guiState

evtAutoLayoutがevtAutoLayoutにフィードするguiStateにフィードすることがわかります。つまり、そこにサイクルがあります。これは意図的なものです。自動レイアウトは、平衡に達するまでGUI状態を調整し、その後Nothingを返すため、ループを停止する必要があります。もちろん、新しいモデルの変更で再び開始できます。

ただし、これをまとめると、コンパイル関数呼び出しで無限ループが発生します。autoLayout = Nothingの場合でも、コンパイル中にスタックオーバーフローが発生します。

guiStateでユニオン呼び出しを削除し、画像からevtAutoLayoutを削除すると...

 guiState :: Discrete GuiState
 guiState = stepperD (GuiState []) $ mkGuiState <$> changes model

それはうまくいきます。

助言がありますか?

4

1 に答える 1

15

質問

リアクティブバナナライブラリは、再帰的に定義されたイベントをサポートしていますか?

1つだけでなく3つの答えがあります。簡単な答えは次のとおりです。1。通常はいいえ、2。時々はい、3。回避策はあります。

ここに長い答えがあります。

  1. リアクティブバナナのセマンティクスは、それ自体の観点から直接定義することをサポートしていませんEvent

    これは、コナル・エリオットが元のFRPセマンティクスで行った決定であり、私はそれに固執することにしました。その主な利点は、セマンティクスが非常に単純なままであるということです。

    type Behavior a = Time -> a
    type Event    a = [(Time,a)]
    

    このモデルをほぼ正確に実装するモジュールReactive.Banana.Modelを提供しました。リアクティブバナナのセマンティクスに関する質問については、そのソースコードを参照してください。特に、これを使用して例について推論することができます。ペンと紙を使用して計算するか、GHCiで(いくつかのモックデータを使用して)試してみると、値evtAutoLayoutが等しい_|_、つまり未定義であることがわかります。

    後者は意外かもしれませんが、あなたが書いたように、例は確かに未定義です。GUIの状態はevtAutoLayoutイベントが発生した場合にのみ変化しますが、GUIの状態が変化するかどうかがわかっている場合にのみ発生する可能性があります。小さな遅延を挿入して、絞扼性フィードバックループを解除する必要があります。[(Time,a)]残念ながら、reactive-bananaは現在、小さな遅延を挿入する方法を提供していません。これは主に、再帰を可能にする方法でモデルの観点から小さな遅延を説明する方法がわからないためです。(ただし、回答3を参照してください。)

  2. イベントを再度参照するEventaの観点からを定義することは可能であり、推奨されます。Behaviorつまり、ビヘイビアーを実行する限り、再帰は許可されます。

    簡単な例は

    import Reactive.Banana.Model
    
    filterRising :: (FRP f, Ord a) => Event f a -> Event f a
    filterRising eInput = eOutput
        where
        eOutput  = filterApply (greater <$> behavior) eInput
        behavior = stepper Nothing (Just <$> eOutput)
    
        greater Nothing  _ = True
        greater (Just x) y = x < y
    
    example :: [(Time,Int)]
    example = interpretTime filterRising $ zip [1..] [2,1,5,4,8,9,7]
    -- example = [(1.0, 2),(3.0, 5),(5.0, 8),(6.0, 9)]
    

    イベントストリームが与えられると、関数filterRisingは以前に返されたイベントよりも大きいイベントのみを返します。これは、関数のドキュメントでstepper示唆されています。

    ただし、これはおそらくあなたが望む種類の再帰ではありません。

  3. それでも、リアクティブバナナに小さな遅延を挿入することは可能です。これはコアライブラリの一部ではないため、保証されたセマンティクスは付属していません。また、それを行うには、イベントループからのサポートが必要です。

    たとえば、wxTimerを使用して、現在のイベントを処理した直後に発生するイベントをスケジュールできます。Wave.hsの例は、reactive-bananaでwxTimerを再帰的に使用する方法を示しています。タイマー間隔をに設定するとどうなるかはよくわかりませんが0、実行が早すぎる可能性があります。おそらく、良い解決策を見つけるために少し実験する必要があります。

お役に立てば幸いです。説明、例などをお気軽にお問い合わせください。

開示:私はリアクティブバナナライブラリの作者です。

于 2011-10-21T16:21:29.743 に答える