2010-09-16 3 views
3

Il doit y avoir quelqu'un là-bas qui a déjà résolu cela. Imaginez que j'ai une classe qui soulève périodiquement un événement sur le changement d'une valeur (par exemple PropertyChanged) Cette valeur n'est rien d'autre qu'une somme d'argent. Maintenant, je voudrais utiliser Rx pour obtenir la somme de l'augmentation de ces 10 dernières minutes. par exemple. BufferWithTime n'aide pas, car j'ai toujours besoin des 10 dernières minutes.Somme courante avec Rx

Des idées comment je peux le faire?

tia Martin

Répondre

3

La solution consiste à maintenir en dessous de l'état des données d'événements pertinents dans les dix dernières minutes dans une liste à l'aide Observable.Scan. L'état est maintenu sous la forme d'une liste de tuples avec un int (argent) et un DateTime comme valeurs.

var events = Observable.FromEvent<YourEventArgs>(
    h => SomeEvent += h, h => SomeEvent -= h); 
var runningSums = 
    events.Scan(new List<Tuple<int, DateTime>>(), 
       (l, e) => 
       { 
        var now = DateTime.Now; 
        // Add last event data to list. 
        l.Add(Tuple.Create(e.EventArgs.Money, now)); 
        // Return the correct part of the list (everything 
        // from the last ten minutes). 
        return l.Where(t => (now - t.Item2) < 
            TimeSpan.FromMinutes(10)).ToList(); 
       }) 
      .Select(l => l.Sum(t => t.Item1)); 
runningSums.Subscribe(sum => Console.WriteLine(sum)); 

EDIT: Exemple qui ne renvoie pas une nouvelle liste pour chaque événement:

var events = Observable.FromEvent<YourEventArgs>(
    h => SomeEvent += h, h => SomeEvent -= h); 
var runningSums = 
    events.Scan(Tuple.Create(new List<Tuple<int, DateTime>>(), 
          DateTime.Now - TimeSpan.FromMinutes(10)), 
       (l, e) => 
       { 
        var now = DateTime.Now; 
        l.Item1.Add(Tuple.Create(e.EventArgs.Nr, now)); 
        // if (trimming-condition) then trim front of list... 
        return Tuple.Create(l.Item1, now - TimeSpan.FromMinutes(10)); 
       }) 
      .Select(l => l.Item1.Where(t => t.Item2 > l.Item2).Sum(t => t.Item1)); 
runningSums.Subscribe(sum => Console.WriteLine(sum)); 
+0

pas mal pour vous et fait ce qu'il faut. MAIS vous pourriez vouloir vérifier si c'est vraiment une bonne idée de retourner une nouvelle liste avec CHAQUE nouvel élément traité. – pointernil

+1

Je ne suis pas entièrement satisfait de cela aussi. Mais, vous devez avoir une liste de valeurs avec horodatage pour pouvoir le faire. Et malgré la liste mutable, c'est une approche entièrement fonctionnelle. Ce que vous pouvez également faire est de ne pas créer une nouvelle liste à chaque fois mais de conserver la même liste et de faire en sorte que l'heure de début (toujours 10 minutes dans le passé) fasse partie de l'état de la méthode 'Scan'. Cependant, maintenant la liste ne cesse de grandir et vous devrez trouver un moyen de réduire le front. J'ai ajouté un exemple pour ceci aussi (sans le rognage, mais ceci peut être facilement ajouté). –

1

Eh bien, consultez la solution suivante. Il s'appuie sur la solution présentée précédemment, mais laisse tomber le pur style fonctionnel pour des raisons d'efficacité (et de lisibilité, je pense). Il réutilise aswell le type construit Timestamped pour suivre le calendrier ...

acclamations

public static class RxEntentsions 
     { 
      class TimeLimitedList<T> 
      { 
       public List<Timestamped<T>> Values = new List<Timestamped<T>>(); 
       TimeSpan span; 
       public TimeLimitedList(TimeSpan sp) { span = sp; } 
       public void Add(Timestamped<T> v) 
       { 
        Values.Add(v); 
        Values.RemoveAll(a => a.Timestamp < (DateTime.Now - span)); 
       } 
      } 

      public static IObservable<List<Timestamped<TSource>>> SlidingWindow<TSource>(this IObservable<Timestamped<TSource>> source, TimeSpan slidingWindow) 
      { 
       return source.Scan0(new TimeLimitedList<TSource>(slidingWindow), (acc, v) => { acc.Add(v); return acc; }).Select(a => a.Values); 
      } 
     } 


    static void Main(string[] args) 
    { 
     var gen = Observable.Interval(TimeSpan.FromSeconds(0.25d)).Timestamp(); 
     gen.SlidingWindow(TimeSpan.FromSeconds(1)).Subscribe(slw => {slw.ForEach(e=> Console.WriteLine(e)); Console.WriteLine("--------");}); 
     Console.ReadLine(); 
    }