Skip to content

A demonstration of how to combine consumers of IEnumerables, aka CoEnumerables

License

Notifications You must be signed in to change notification settings

banbh/CoEnumerable

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Introduction

Suppose you have a sequence of numbers:

var nums = Enumerable.Range(1, 5); // 1,2,3,4,5

Then nums.Min() computes their minimum, and nums.Max() computes their maximum. However, if we want want both the mininum and the maximum we end up running through nums twice. We could, of course, write our own function MinMax() which keeps track of both the minimum and the maximum. But let's assume what we want to compute two, more complicated, functions of nums and that we don't have the source code for the function. Suppose, also, that the sequence we are working with is expensive to produce (so we don't want to run through it more than once) and large (so we can't store all the items in memory). Is there a way to still compute what we wanted? This repo shows how one can go about doing this.

This repo was inspired by the following question on SO: Consuming an IEnumerable multiple times in one pass.

Combining CoEnumerables

Let's call an object with type IEnumerable<S> an enumerable, and an object with type Func<IEnumerable<S>, T> a coenumerable (since it is dual to an enumerable). Each time we call GetEnumerator() on enumerable we say that we are enumerating it. If we call MoveNext() on the resulting IEnumerator<S> until it returns false then we say we enumerated fully, otherwise we enumerated partially.

We can now state precisely what we described informally in the Introduction. We want a procedure to evaluate two coenumerables on a given enumerable so that

  • the enumerable is only enumerated once,
  • we do not store the items of the enumerable simultaneously in memory,
  • we do not require access to the source code of the coenumerables, and
  • if both coenumerables enumerate the enumerable partially, then so too does the procedure.

The Combine extension method in the CoEnumerable project in this repo does exactly this. It turns out to be fairly simple to implement in terms of a Barrier (not to be confused with a MemoryBarrier).

Comments and Limitations

  • It is not very fast (as CoEnumerable.Demo illustrates)
  • In theory a coenumerable could do either of two things which will cause problems for this library:
    • A single evaluation of the coenumerable on an enumerable might enumerate it multiple times
    • The coenumerable could fail to Dispose() the enumerator it uses to enumerate the enumerable
  • Could this be implemented using ReactiveX? It is certainly possible. In fact there is at least one SO answer that proposes this as a solution to a similar question: Reading an IEnumerable multiple times. However, I am not familiar enough with ReactiveX to be certain whether it is, or is not possible. In particular, I don't know whether it is possible to satisfy the last requirement of the four listed above (if you drop that last requirement I think it is possible).

About

A demonstration of how to combine consumers of IEnumerables, aka CoEnumerables

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages