0

ビジネスロジックは、再帰的に定義されたオブザーバブルによって自然にモデル化できるように見えることがあります。以下に一例を示します。

interface Demo {
    IObservable<CommandId> userCommands;
    IObservable<IObservable<IProcessingState>> processes;
    IObservable<CommandId> skippedCommands;
    IObservable<(CommandId, CommandResult)> RunCommand(CommandId id);
}

interface IProcessingState {
    bool IsProcessing {get;}
    CommandId? ProcessingId {get;}
}

コマンドのユーザー入力ごとに、prcocess で実行中のプロセスをトリガーするか、skipedCommands で 1 つの値を発行する必要があります。このロジックの直訳かもしれません

var validCommands = userCommands.WithLatestFrom(processes).Where(x => !x.Item2.IsProcessing)
var skippedCommands = userCommands.WithLatestFrom(processes).Where(x => x.Item2.IsProcessing)
var processes = validCommands.Select(c => RunCommand(c))

上記のコードで示されているように、validCommandsとの代入processesは相互再帰的ですprocesses

var processes = userCommands.WithLatestFrom(processes)
                            .Where(x => !x.Item2.IsProcessing)
                            .Select(c => RunCommand(c))

prcessesただし、このように C# で Observable を定義することはできません。

関連する可能性のあるものをいくつか見つけました。

  1. Observable.Generateコンストラクタ。userCommandsただし、同期の方法で独自の状態に折りたたまれているようです。オブザーバブルとRunCommandインの使用方法がわかりませんObservable.Generate

  2. Rx.Net はこの演算子を提供しませんでしたが、 FSharp.Control.Reactive のようにこれらの演算子を提供するいくつexhaustのサードパーティ ライブラリがあります。実装は次のようなものですexhaustMap

let exhaustMap f source =
        Observable.Create (fun (o : IObserver<_>) -> 
            let mutable hasSubscription = false
            let mutable innerSub = None
            let onInnerCompleted () =
                hasSubscription <- false
                innerSub |> Option.iter Disposable.dispose
            let onOuterNext x =
                if not hasSubscription then
                    hasSubscription <- true 
                    f x |> subscribeSafeWithCallbacks 
                            o.OnNext o.OnError onInnerCompleted
                        |> fun y -> innerSub <- Some y
            source
            |> subscribeSafeWithCallbacks
                onOuterNext o.OnError o.OnCompleted)

ただし、2 つの問題があります。を。この演算子を直接使用することは上記の要件に適合しません。スキップされたコマンドは黙って無視されます。要件に合わせてソース コードを少し変更することはできますが、まだ別の問題があります。この実装では、2 つのローカル可変変数と、2 つのネストされたサブスクリプションが導入されました。これがすべての場合に問題ないかどうかはわかりません (データ競合のリスクはありますか?)。変更可能な参照以外の演算子の構成に基づくソリューションを好みます。

  1. SodiumFRPは、前方参照タイプStreamLoopと を提供しCellLoopました。また、Functional Reactive Programming の本によると、これらの前方参照型の Rx の代替は、上記の再帰構造Subjectを使用することにより、Subject2 つのフェーズに分けられます。問題は、Intro to Rx で示されています。使用Subjectすると、より多くの状態を手動で管理する必要があり、少なくともサブジェクトを破棄する必要があり、おそらくホット オブザーバブルを強制する必要があります。使用せずに解決策が存在するかどうか疑問に思っていますSubject

  2. 結果windowの最後の値 (終了の直前) に境界を持つ演算子を使用すると、上記はある程度構築できますが、この解決策では終了信号を 2 回使用する必要があり、慎重な処理が必要です (試行と調整でしばらく静かにする必要があります, , , , オーバーロード同時に発生するイベントで必要な結果を取得するためのオペレーターの数)。RunCommandprocessesTake(1)zipwithLatestFromcombineLatestWindow

特に演算子のみを使用して、この問題に対する上記の解決策のより良い解決策または変更はありますか?

4

1 に答える 1