2

This is not real life example (and this code will probably not compile) but I'm trying to make it a little bit simpler than the problem I actually have.

Let's say I have collection of images:

private void IEnumerable<Image> GetImages()
{
    foreach (var filename in GetFilenames())
    {
         yield return Image.LoadFile(filename);
    }
}

and I would like to show slideshow driven by user pressing 'space':

var images = Observable.FromEvent(form, "KeyPress")
  .Where(e => e.KeyCode == KeyCode.Space)
  .Zip(GetImages.ToObservable(), (k, i) => i);

And this kind of works. It does emit next image when space is pressed. The problem is it actually loads them at full speed, so they get buffered and consume a lot of memory (and processing power when loading). I could feed filtered-key-presses into GetImages and to the zipping there but I would not preserve purity of GetImages.

Is there any way to prevent enumerable.ToObservable() from being enumerated in advance if not needed?

Another example (this one will compile):

var observable = 
    Observable.Interval(TimeSpan.FromSeconds(1))
    .Zip(
        Observable.Range(0, 1000000).Do(x => Console.WriteLine("produced {0}", x)), 
        (_, v) => v
    );

var subscription = observable.Subscribe(x => Console.WriteLine("consumed {0}", x));

Console.WriteLine("Press <enter>...");
Console.ReadLine();

It will generate a lot of "produced" (in advance) but only one "consumed" per second.

Milosz Krajewski
  • 1,160
  • 1
  • 12
  • 19

2 Answers2

6

Dave's idea of sticking to pulling images from IEnumerable<T> is solid, but you can achieve the same goal much more easily - just delete the ToObservable() from your code:

var images = Observable.FromEvent(form, "KeyPress")
                       .Where(e => e.KeyCode == KeyCode.Space)
                       .Zip(GetImages() /* No ToObservable() here! */, (k, i) => i);

This overload of Zip will drive the image enumerable as you press keys.

James World
  • 29,019
  • 9
  • 86
  • 120
  • 1
    +1. I just miss one clear statement saying: observable.Zip(enumerable) is about providing enumerable at the pace of observable (you can imply from this answer, but it is not explicitly said). – Milosz Krajewski Oct 08 '14 at 10:51
  • +1 Another 'hidden' overload in Rx and I've never used this one before. Should have checked the overload list before posting -- newbie mistake ;-) – Dave Sexton Oct 08 '14 at 15:57
  • As they say - I've been there, done that. Will no doubt be visiting again soon. :) – James World Oct 08 '14 at 23:50
0

How about pulling from an enumerator instead?

var keys = Observable.FromEventPattern<KeyPressEventArgs>(form, "KeyPress");
var slides = (from key in keys
              where key.EventArgs.KeyCode == KeyCode.Space
              select key)
             .Publish(spaces => 
              {
                var images = GetImages().GetEnumerator();

                return spaces.TakeUntil(from _ in spaces
                                        where !images.MoveNext()
                                        select Unit.Default)
                             .Select(_ => images.Current);
              });
Dave Sexton
  • 2,562
  • 1
  • 17
  • 26