Промежуточная сумма с Rx - PullRequest
       0

Промежуточная сумма с Rx

3 голосов
/ 16 сентября 2010

Должен быть кто-то, кто уже решил это. Представьте, что у меня есть класс, который периодически вызывает событие об изменении значения (например, PropertyChanged) Эта ценность - не что иное, как сумма денег.

Теперь я хотел бы использовать Rx, чтобы получить сумму приращения за последние 10 минут. например BufferWithTime не помогает, так как мне всегда нужны последние 10 минут.

Есть идеи, как мне это сделать?

ТИА Martin

Ответы [ 2 ]

3 голосов
/ 16 сентября 2010

Решение, приведенное ниже, включает сохранение состояния соответствующих данных события за предыдущие десять минут в списке с использованием Observable.Scan. Состояние поддерживается в виде списка кортежей с int (деньги) и DateTime в качестве значений.

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 : пример, который не возвращает новый список для каждого события:

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));
1 голос
/ 17 сентября 2010

Хорошо, проверьте следующее решение.Он основан на ранее представленном здесь решении, но отбрасывает чистый функциональный стиль ради эффективности (и, думаю, удобочитаемости).Он также использует встроенный тип Timestamped для отслеживания времени ...

cheers

    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();
    }
...