views:

35

answers:

1

I'm playing around with using LINQ to Objects for multiplexing and demultiplexing but it seems to me that this is a pretty tricky problem.

See this demuxer signature:

public static IEnumerable<IEnumerable<TSource>> Demux<TSource>(this IEnumerable<TSource> source, int multiplexity)

On an abstract level this is easy but ideally one would want to

  • remain lazy for the source stream
  • remain lazy for each multiplexed stream
  • not reiterate over the same elements

How would you do this?

I'm a bit tired so it could be my concentration failing me here...

+2  A: 

Assuming that you want (0, 1, 2, 3) to end up as (0, 2) and (1, 3) when demuxing to two streams, you basically can't do it without buffering. You could buffer only when necessary, but that would be difficult. Basically you need to be able to cope with two contradictory ways of using the call...

Getting both iterators, and reading one item from each of them:

// Ignoring disposing of iterators etc
var query = source.Demux(2);
var demuxIterator = query.GetEnumerator();
demuxIterator.MoveNext();
var first = demuxIterator.Current;
demuxIterator.MoveNext();
var second = demuxIterator.Current;
first.MoveNext();
Console.WriteLine(first.Current); // Prints 0
second.MoveNext();
Console.WriteLine(second.Current); // Prints 1

Or getting one iterator, then reading both items:

// Ignoring disposing of iterators etc
var query = source.Demux(2);
var demuxIterator = query.GetEnumerator();
demuxIterator.MoveNext();
var first = demuxIterator.Current;
first.MoveNext();
Console.WriteLine(first.Current); // Prints 0
first.MoveNext();
Console.WriteLine(first.Current); // Prints 2

In the second case, it has to either remember the 1, or be able to reread it.

Any chance you could deal with IList<T> instead of IEnumerable<T>? That would "break" the rest of LINQ to Objects, admittedly - lazy projections etc would be a thing of the past.

Note that this is quite similar to the problem that operations like GroupBy have - they're deferred, but not lazy: as soon as you start reading from a GroupBy result, it reads the whole of the input data.

Jon Skeet
Yes I can define it however I like as it is a hobby project. But in the same spirit I want to have the optimal representation. But I think there's a fundamental mechanism missing in the C# language to allow this to happen. It's quite an interesting case for testing the expressiveness of a language I think.Good point about GroupBy. It would be truly wonderful if we could have real laziness here.And also exceptions - yes that is even more tricky because you can't use "using" when you don't know how many nested streams you have to deal with.<headache/>
Bent Rasmussen
I think you're right with buffering. Maybe create a custom IList with a fixed set of members each working off the same enumerable and respecting order. It's a shame you can't express it directly but it's probably good enough.
Bent Rasmussen
@Bent: It's not that anything's missing in the language - it's a fundamental mismatch between what you're trying to do and the "forward only" nature of the source mux. When you have two iterators (one for each of the demuxed sequences) the caller could ask for the next item from either of them - how would you expect to cope with that if you don't have random access and don't want to buffer or skip over data?
Jon Skeet
I was thinking of a "parallel" but dependent enumeration abstraction. But disregard, it can be expressed in the language.
Bent Rasmussen