views:

42

answers:

2

With that I mean similar to the Linq join, group, distinct, etc. only working on sequences of values, not collections.

The difference between a sequence and a collection is that a sequence might be infinite in length, whereas a collection is finite.

Let me give you an example:

var c1 = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var c2 = FunctionThatYieldsFibonacciNumbers();

var c3 = c1.Except(c2);

This does not work. The implementation of Except does not work on the basis that the numbers in either collection will be strictly ascending or descending, so it first tries to gather all the values from the second collection into a set (or similar), and only after that will it start enumerating the first collection.

Assuming that the function above is just a While-loop that doesn't terminate unless you explicitly stop enumerating it, the above code will fail with a out-of-memory exception.

But, given that I have collections that are considered to be strictly ascending, or descending, are there any implementations already in .NET 4.0 that can do:

  1. Give me all values common to both (inner join)
  2. Give me all values of both (union/outer join)
  3. Give me all values in sequence #1 that isn't in sequence #2

I need this type of functionality related to a scheduling system I need to build, where I need to do things like:

c1 = the 1st and 15th of every month from january 2010 and onwards
c2 = weekdays from 2010 and onwards
c3 = all days in 2010-2012
c4 = c1 and c2 and c3

This would basically give me every 1st and 15th of every month in 2010 through 2012, but only when those dates fall on weekdays.

With such functions it would be much easier to generate the values in question without explicitly having to build collections out of them. In the above example, building the first two collections would need to know the constraint of the third collection, and the examples can become much more complex than the above.

+2  A: 

I'd say that the LINQ operators already work on general sequences - but they're not designed to work specifically for monotonic sequences, which is what you've got here.

It wouldn't be too hard to write such things, I suspect - but I don't believe anything's built-in; there isn't even anything for this scenario in System.Interactive, as far as I can see.

Jon Skeet
I'm already on my way to write them, I just encountered the `Except` method, which was what I planned on naming mine, but of course that won't work, so I tested it, and it fails with the out-of-memory exception so for my cases it isn't usable. All I wanted to know was whether I was barking up the wrong tree.
Lasse V. Karlsen
I guess I will drop extension method syntax here, to make it explicit that this isn't Linq, then there is no naming conflict.
Lasse V. Karlsen
+1  A: 

You may onsider the Seq module of F#, which is automatically invoked by using special F# language constructs like 1 .. 10 which generates a sequence. It supports infinite sequences the way you describe, because it allows for lazy evaluation. Using F# may or may not be trivial in your situation. However, it shouldn't be too hard to use the Seq module directly from C# (but I haven't tried it myself).

Following this Mandelbrot example shows a way to use infinite sequences with C# by hiding yield. Not sure it brings you closer to what you want, but it might help.

EDIT
While you already commented that it isn't worthwhile in your current project and accepted an answer to your question, I was intrigued by the idea and conjured up a little example.

It appeared to be rather trivial and works well in C# with .NET 3.5 and .NET 4.0, by simple including FSharp.Core.dll (download it for .NET 3.5) to your references. Here's an out-of-the box example of an infinite sequence implementing your first use-case:

// place in your using-section:
using Microsoft.FSharp.Collections;
using Microsoft.FSharp.Core;

// [...]

// trivial 1st and 15th of the month filter, starting Jan 1, 2010.
Func<int, DateTime> firstAndFifteenth = (int i) =>
{
    int year = i / 24 + 2010;
    int day = i % 2 != 0 ? 15 : 1;
    int month = ((int)i / 2) % 12 + 1;
    return new DateTime(year, month, day);
};

// convert func to keep F# happy
var fsharpFunc = FSharpFunc<int, DateTime>.FromConverter(
                   new Converter<int, DateTime>(firstAndFifteenth));

// infinite sequence, returns IEnumerable
var infSeq = SeqModule.InitializeInfinite<DateTime>(fsharpFunc);

// first 100 dates
foreach (var dt in infSeq.Take(100))
    Debug.WriteLine("Date is now: {0:MM-dd-yyy}", dt);

Output is as can be expected, first few lines like so:

Date is now: 01-01-2010
Date is now: 01-15-2010
Date is now: 02-01-2010
Date is now: 02-15-2010
Date is now: 03-01-2010
Abel
Definitely worth a look, but unfortunately not an option for this particular project.
Lasse V. Karlsen