2012-05-03 11 views
5

私はシンプルなペアクラスの大規模なコレクションを持っています。私は適切な時にリスト内の各項目の値(例:アクション<ダブル>)を持つイベントをトリガーしたいと思います。時は過去ですから、リストの最初のものが「今」になるようにタイムスタンプを正規化する必要があります。 Reactive Extensionsでこれを設定して、2つのアイテム間の時間差の後に次のイベントをトリガーすることはできますか?RXを使用してさまざまな時期にイベントをトリガーしますか?</p> <pre><code>public class Pair { public DateTime Timestamp; public double Value; } </code></pre> <p>彼らが昇順のタイムスタンプでソートされています

+0

http://reactiveproperty.codeplex.com/をご覧ください。 – dwerner

答えて

6

セイpairsはあなたの順序である:今

var obs = pairs.OrderBy(p => p.Timestamp).ToObservable(); 

obsは、注文した観測可能とペアです。

Observable.Zip(
    obs, 
    obs.Take(1).Concat(obs), 
    (pair1, pair2) => Observable.Timer(pair1.Timestamp - pair2.Timestamp) 
     .Select(_ => pair1.Value)) 
.Concat() 
.Subscribe(/* Do something here */); 

ジップは絶対時間をオフセットに変換します。この新しい値はその後、それを適切な量を遅らせるためにObservable.Timerに入れて

Original 1--2--4--7--11 
Offset 1--1--2--4--7--11 
Joined 0--1--2--3--4 

次のようにそれは、1でシーケンスを取り、自分自身でそれに参加するが、オフセットされます。最後のConcatIObservable<IObservable<double>>の結果をIObservable<double>にフラット化します。これはあなたのシーケンスが注文されていると仮定します。

+0

良い解決策。私は 'var orderedObs = pairs.OrderBy(p => p.Timestamp).ToObservable()'を追加して、何が起こる必要があるかを明らかにし、代わりにそれを使用します。私はこれらの変更を行った。 – yamen

+0

これは多くの助けになる。私は履歴データを照会し、元々記録されたようにそれを再生するために使用しました。新しいシステムが動作することを証明するシミュレータ。 –

+0

正確に何が起こっていたのか把握するのにちょっと時間をかけましたが、今すぐ取得します。 Rxはマインド***です。しかし、素晴らしい解決策。 +1 – BFree

0

私はこの問題が面白いと思う、これは私が最初にそれに行くだろう。

static void RunPairs(IEnumerable<Pair> pairs, Action<double> pairEvent) 
{ 
    if (pairs == null || !pairs.Any() || pairEvent == null) 
    return; 

    // if we can promise the pairs are already sorted 
    // obviously we don't need this next line 
    pairs = pairs.OrderBy(p => p.Timestamp); 
    var first = pairs .First().Timestamp; 
    var wrapped = pairs.Select(p => new { Offset = (p.Timestamp - first), Pair = p }); 

    var start = DateTime.Now; 

    double interval = 250; // 1/4 second 
    Timer timer = new Timer(interval); 

    timer.AutoReset = true; 
    timer.Elapsed += (sender, elapsedArgs) => 
    { 
    var signalTime = elapsedArgs.SignalTime; 
    var elapsedTime = (signalTime - start); 

    var pairsToTrigger = wrapped.TakeWhile(wrap => elapsedTime > wrap.Offset).Select(w => w.Pair); 
    wrapped = wrapped.Skip(pairsToTrigger.Count()); 

    if (!wrapped.Any()) 
     timer.Stop(); 

    foreach (var pair in pairsToTrigger) 
     pairEvent(pair.Value);  
    }; 

    timer.Start(); 
} 
+0

これは、Rxが 'Timer'、' Defer'、 'Delay'のような拡張子を持っているので、本当に不必要に複雑です。 – yamen

+0

@yamen私はRxを使ったことはありません。私はそれが間違っていると思ったので挑戦として最初からやる方法に対応したいと思っていました:)この文脈での私の答えが単なるスパムであれば申し訳ありません。 – payo

+2

申し訳ありませんが、上記のRxソリューションから何かを学ぶことを願っています。あなたの答えは、Rxがすばらしい理由の例として実際に役立ちます:-) – yamen

2

「のRxを使用」により、あなたは、これは非常に簡単な解決策である、私はただのRxスケジューラを使用することを許可する場合:

Action<double> action = 
    x => 
     Console.WriteLine(x); 

var ts0 = pairs.Select(p => p.Timestamp).Min(); 

pairs 
    .ForEach(p => 
     Scheduler 
      .ThreadPool 
      .Schedule(
       p.Timestamp.Subtract(ts0), 
       () => action(p.Value))); 

これはSystem.Interactive拡張ForEachを使用しますが、あなただけ使用することができます通常のforeachループを使用してスケジューラをロードします。

は、私は、次のダミーデータでコードをテストしてみた:

var pairs = new [] 
{ 
    new Pair { Timestamp = new DateTime(2011, 1, 1, 7, 12, 30), Value = 1.1, }, 
    new Pair { Timestamp = new DateTime(2011, 1, 1, 7, 12, 45), Value = 1.2, }, 
    new Pair { Timestamp = new DateTime(2011, 1, 1, 7, 12, 40), Value = 1.3, }, 
}; 

私はこのことができます願っています。

+0

スケジューラには独自のキューがありますか?または、このコードはスレッドプール全体を噛み砕くでしょうか?私はこのソリューションのスケーラビリティが心配です。 – Brannon

+0

@Brannon - スケジューラが内部的にヒープソートを使用して、アクションをキューに入れていると正しく思い出しています。また、スケジューラは一度に1つのアクションしか実行せず、別のアクションがすぐに実行できるようになったら現在のスレッドを再利用します。したがって、スレッドは一度に1つのスレッドしか使用しません。 – Enigmativity

関連する問題

 関連する問題