Trouble Implementing a Sliding Window in Rx

后端 未结 4 829
温柔的废话
温柔的废话 2020-12-15 09:38

I created a SlidingWindow operator for reactive extensions because I want to easily monitor things like rolling averages, etc. As a simple example, I want to s

4条回答
  •  刺人心
    刺人心 (楼主)
    2020-12-15 10:20

    The sliding window implementations here was not adequate for my idea of a sliding window. The closest was using Buffer(N, 1) but is a problem since it waits for the first N items before it emits the first result then slides beyond the end of the sequence. I want up to N items emitted at a time.

    I ended up with this implementation:

    public static IObservable> SlidingWindow(this IObservable obs, int windowSize) =>
        Observable.Create>(observer =>
        {
            var buffer = new CircularBuffer(windowSize);
            return obs.Subscribe(
                value =>
                {
                    buffer.Add(value);
                    observer.OnNext(buffer.ToList());
                },
                ex => observer.OnError(ex),
                () => observer.OnCompleted()
            );
        });
    

    I was initially using a queue for the buffer but wanted to use something a bit more lightweight.

    public class CircularBuffer : IReadOnlyList
    {
        private readonly T[] buffer;
        private int offset;
        private int count;
        public CircularBuffer(int bufferSize) => this.buffer = new T[bufferSize];
        public int Capacity => buffer.Length;
        public int Count => count;
        public T this[int index] => index < 0 || index >= count
            ? throw new ArgumentOutOfRangeException(nameof(index))
            : buffer[(offset + index) % buffer.Length];
        public void Add(T value)
        {
            buffer[(offset + count) % buffer.Length] = value;
            if (count < buffer.Length) count++;
            else offset = (offset + 1) % buffer.Length;
        }
        public IEnumerator GetEnumerator()
        {
            for (var i = 0; i < count; i++)
                yield return buffer[(offset + i) % buffer.Length];
        }
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }
    

    It would yield sequences for Observable.Range(0, 10).SlidingWindow(3):

     0,1,2,3,4,5,6,7,8,9
    [0]
    [0,1]
    [0,1,2]
      [1,2,3]
        [2,3,4]
          [3,4,5]
            [4,5,6]
              [5,6,7]
                [6,7,8]
                  [7,8,9]
    

提交回复
热议问题