ploeh / ploeh.github.com

ploeh blog 'source code'
133 stars 323 forks source link

Infinite Enumerables in C# #927

Open WorldMaker opened 1 year ago

WorldMaker commented 1 year ago

Regarding: https://blog.ploeh.dk/2023/11/14/cc-golf/

(Sorry, the comment formatting seemed a bit much for me to figure out just for this quick note.)

If there's a general API in .NET that corresponds to the F#-specific Seq.initInfinite I haven't found it

In System.Interactive (Ix.NET) there is an extension operator Enumerable.Repeat(value) that (truly [^0]) infinitely repeats a single value (without using System.Interactive there is a Repeat that requires a count).

[^0]: The source code shows it is exactly the simple generator you would think: https://github.com/dotnet/reactive/blob/main/Ix.NET/Source/System.Interactive/System/Linq/Operators/Repeat.cs

ploeh commented 1 year ago

How would you use that to enumerate a range?

WorldMaker commented 1 year ago

Ah, the fun of dot-free styles and missing arguments were used.

If you wanted to do it a fun way, with Ix.NET you could always do:

var initInfinite = Enumerable.Repeat(1).Scan((acc, cur) => acc + cur, -1);

Though I don't think that's going to gain you much over Enumerable.Range(0, int.MaxValue) at that point, which is the effective limit anyway (unless you are trying for intentional overflow and want to throw in an unchecked { } in that Scan).

ploeh commented 1 year ago

I thad to modify that expression before I could make it work:

var initInfinite = EnumerableEx.Repeat(1).Scan(-1, (acc, cur) => acc + cur);

If one wants to take on a dependency on Ix, though, one could also use Generate:

var initInfinite = EnumerableEx.Generate(0, _ => true, x => x + 1, x => x);

It's approximately the same number of characters, though.

But nothing prevents one from just writing the generator oneself:

public static class EnumerablePloeh
{
    public static IEnumerable<T> Iterate<T>(T seed, Func<T, T> func)
    {
        var current = seed;
        while (true)
        {
            yield return current;
            current = func(current);
        }
    }
}

which can be used like this:

var initInfinite = EnumerablePloeh.Iterate(0, x => x + 1);

I've patterned that generator over Haskell's iterate function, rather than F#'s Seq.initInfinite, since the former is more general.