RX - Zip输出意外结果

时间:2015-04-20 18:02:06

标签: c# reactive-programming

请帮我理解一个现象:

为什么X不等于Observable项目中的索引?

构建块例如:

        public class EcgSample
        {               
            public EcgSample(int y)
            {
                Y = y;   
            } 

            public int X { get; set; }
            public int Y { get; set; }  
        }

        private void Print(Tuple<EcgSample, int> s)
        {
              Debug.WriteLine("X : {0} , Y : {1} , Index : {2}", s.Item1.X, s.Item1.Y, s.Item2);
        }

        private List<EcgSample> CreateSamples()
        {
            var testSamples = new List<EcgSample>();

            for (short i = 0; i < 1400; i++)
            {
               testSamples.Add(new EcgSample(i));   
            }

            return testSamples;
        }

示例observable:(输出预期结果)

       // (1) Create From Collection .
       IObservable<EcgSample> sampleObservable = CreateSamples().ToObservable(new EventLoopScheduler());

       // (2) Repeat 
       IObservable<EcgSample> repeated = sampleObservable.Repeat();

       // (3) Indexed 
       IObservable<Tuple<EcgSample,int>> indexed = repeated.Select((item, index) =>
       {
           item.X = index;
           return new Tuple<EcgSample, int>(item, index);
       }); 

       // (4) Buffered 
       IObservable<IList<Tuple<EcgSample, int>>> buffered = indexed.Buffer(250); 

       // (5) SelectMany and Print .
       _disposable = buffered.SelectMany(buf => buf).Subscribe(Print);

OUTPUT:这是Observable序列的预期输出。

       [8384] X : 0 , Y : 0 , Index : 0 
       [8384] X : 1 , Y : 1 , Index : 1 
       [8384] X : 2 , Y : 2 , Index : 2 
       [8384] X : 3 , Y : 3 , Index : 3 
       [8384] X : 4 , Y : 4 , Index : 4 

修改:(不会输出预期的结果)

现在我想要在每个时间间隔内使用每个缓冲区:

     // (5) Create an Observable from a Timer. 
     IObservable<ElapsedEventArgs> timerObservable = Observable.Create<ElapsedEventArgs>(
            observer =>
            {
                var timer = new Timer();
                timer.Interval = 250;
                timer.Elapsed += (s, e) => observer.OnNext(e);
                timer.Start();
                return Disposable.Create(() =>
                {
                    timer.Stop();
                });
            });

        // (6) Zip with the buffer observable 
        IObservable<IList<Tuple<EcgSample, int>>> zipped = timerObservable.Zip(buffered, (t, b) => b);

        // (7) SelectMany and Print .
        _disposable = zipped.SelectMany(buf => buf).Subscribe(Print);

OUTPUT:输出意外结果:注意X不等于索引。

   [9708] X : 187600 , Y : 0 , Index : 0 
   [9708] X : 187601 , Y : 1 , Index : 1 
   [9708] X : 187602 , Y : 2 , Index : 2 
   [9708] X : 187603 , Y : 3 , Index : 3 

任何想法为什么X从187600开始(不用说每次运行我的程序时这个值都不同)..?

编辑:

我通过简单的预测解决了这个问题,但我仍然想知道第一个问题出现的原因。

        List<EcgSample> list = CreateSamples();     

        var loop = new EventLoopScheduler();
        var sampleObservable = list.ToObservable(loop);

        IObservable<EcgSample> reapeted = sampleObservable.Repeat();

        IObservable<IList<EcgSample>> buffered = reapeted.Buffer(250);

        IObservable<ElapsedEventArgs> timerObservable = Observable.Create<ElapsedEventArgs>(
            observer =>
            {
                var timer = new Timer();
                timer.Interval = 250;
                timer.Elapsed += (s, e) => observer.OnNext(e);
                timer.Start();
                return Disposable.Create(() =>
                {
                    timer.Stop();
                });
            });

        IObservable<IList<EcgSample>> zipped = timerObservable.Zip(buffered, (t, b) => b);

        _disposable = zipped.SelectMany(buf => buf).Select((item, index) =>
        {
            item.X = index;
            return new Tuple<EcgSample, int>(item, index);

        }).Subscribe(Print);

1 个答案:

答案 0 :(得分:3)

你的答案显示你可以改变一件事来获得你想要的行为,但这并不是它没有按照你预期的方式工作的原因。

如果要将Observable中的每个条目与一个数字相关联,实际上应该将它与一个数字相关联。您执行此操作的方式,流中的每个元素与数字之间没有实际连接。您的修复只是确保您在下一个项目通过之前处理每个项目,因此数字恰好是正确的值。但这是一个非常不稳定的情况。

如果您只是想要了解您在该广告资源中最常使用的项目,请查看the overload of Select that gives you the index

stream.Select((item, index) => new { item, index })
      .Subscribe(data => Debug.WriteLine("Item at index {0} is {1}", data.index, data.item))

或者,如果您想要的东西与流上的项目数不同,您可以执行以下操作:

stream.Select(item => new { item, index = <some value you calculate> })
...

这样您的对象及其索引就会绑定在一起。您可以在任何未来点使用项目索引,并仍然知道其索引是什么。而您的代码依赖于在处理下一个项目之前获取每个项目。

解决问题中的修改

首先,看看Observable.Interval。它会执行您尝试使用计时器的操作,但更容易。

其次,请看下面的示例,该示例再现了您在问题中所做的事情。运行此代码会生成正确的输出:

var items = Enumerable.Range(65, 26)
                      .Select(i => (char)i)
                      .Repeat();

var observableItems = items.ToObservable()
                           .Select((c, i) => new { Char = c, Index = i });

var interval = Observable.Interval(TimeSpan.FromSeconds(0.25));

var buffered = observableItems.Buffer(10);
var zipped = buffered.Zip(interval, (buffer, _) => buffer);

zipped.SelectMany(buffer => buffer).Dump();

您可以在LinqPad中运行该代码,这是一个非常有用的工具,用于探索Rx(以及.Net的其他部分)。

最后 - 我认为这是一个简化的练习,试图弄清楚你的情况发生了什么。看起来您可能正在尝试处理推送比您想要处理的更多更新的传感器数据。使用Zip与间隔不会对此有所帮助。您将减慢数据的到达速度,但它只会构建一个越来越大的数据队列,等待通过Zip。

如果您想每250毫秒获得一个数据点,请查看Sample。如果您希望一次获得250毫秒的读数,请查看overload of Buffer that takes a timespan instead of a count