ビジネスロジックは、再帰的に定義されたオブザーバブルによって自然にモデル化できるように見えることがあります。以下に一例を示します。
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 を定義することはできません。
関連する可能性のあるものをいくつか見つけました。
Observable.Generate
コンストラクタ。userCommands
ただし、同期の方法で独自の状態に折りたたまれているようです。オブザーバブルとRunCommand
インの使用方法がわかりませんObservable.Generate
。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 つのネストされたサブスクリプションが導入されました。これがすべての場合に問題ないかどうかはわかりません (データ競合のリスクはありますか?)。変更可能な参照以外の演算子の構成に基づくソリューションを好みます。
SodiumFRPは、前方参照タイプ
StreamLoop
と を提供しCellLoop
ました。また、Functional Reactive Programming の本によると、これらの前方参照型の Rx の代替は、上記の再帰構造Subject
を使用することにより、Subject
2 つのフェーズに分けられます。問題は、Intro to Rx で示されています。使用Subject
すると、より多くの状態を手動で管理する必要があり、少なくともサブジェクトを破棄する必要があり、おそらくホット オブザーバブルを強制する必要があります。使用せずに解決策が存在するかどうか疑問に思っていますSubject
結果
window
の最後の値 (終了の直前) に境界を持つ演算子を使用すると、上記はある程度構築できますが、この解決策では終了信号を 2 回使用する必要があり、慎重な処理が必要です (試行と調整でしばらく静かにする必要があります, , , , オーバーロード同時に発生するイベントで必要な結果を取得するためのオペレーターの数)。RunCommand
processes
Take(1)
zip
withLatestFrom
combineLatest
Window
特に演算子のみを使用して、この問題に対する上記の解決策のより良い解決策または変更はありますか?