1
\$\begingroup\$

After receiving some feetback about my previous attempt to create reusable, easily extendable sequence generators I try once again to create such a framework.

This time there is no inheritance. Just extension methods.

The new core is the Sequence class:

public class Sequence
{
    public Sequence(int count) { Count = count; }

    public int Count { get; }

    public static Sequence Generate(int count)
    {
        return new Sequence(count);
    }

    public IEnumerable<T> Generate<T>(Func<IEnumerable<T>> generator) => generator().Take(Count);  
}

By itself it doesn't do much. You need to extend it with your extensions that hook up to it and provide it with the actual generator for your sequence.

Here are some examples for the Fibonacci sequence:

static class FibonacciSequenceExtensions
{
    public static IEnumerable<T> Fibonacci<T>(this Sequence sequence, T firstTwo, T firstStep, Func<T, T, T> sum) 
    {
        return sequence.Generate(() => FibonacciGenerator(firstTwo, firstStep, sum));
    }

    public static IEnumerable<int> Fibonacci(this Sequence sequence, int firstTwo, int firstStep)
    {
        return sequence.Generate(() => FibonacciGenerator(firstTwo, firstStep, (x, y) => x + y));
    }

    public static IEnumerable<TimeSpan> Fibonacci(this Sequence sequence, TimeSpan firstTwo, TimeSpan firstStep)
    {
        return sequence.Generate(() => FibonacciGenerator(firstTwo, firstStep, (x, y) => x + y));
    }

    private static IEnumerable<T> FibonacciGenerator<T>(T firstTwo, T firstStep, Func<T, T, T> sum)
    {
        var preview = firstTwo;
        var current = sum(firstTwo, firstStep);

        yield return preview;
        yield return preview;
        yield return current;

        while (true)
        {
            var newCurrent = sum(preview, current);
            yield return newCurrent;
            preview = current;
            current = newCurrent;
        }
    }
}

Then you can use it like this:

var result = Sequence.Generate(10).Fibonacci(3, 4).ToList();

What's the purpose of this? To have only a single class for all sequences. You don't have to remember where you've implemented some sequence. You can use them all via a single class.


In case someone is interested how the final version looks like I put it here under the previous quesiton because it's the inheritance edition that I decided to keep.

\$\endgroup\$
2
\$\begingroup\$

The API looks ok, IMHO, except for this syntax:

Sequence.Generate(10).Generate(...); // huh?

The downside is that for every sequence, I have to write at least two methods. First, method, to generate an actual sequence (FibonacciGenerator()), second method to wire it up to sequence.Generate (Fibonacci<T>()). This looks like a lot of extra code for what essentially could have been:

SequenceExtensions.FibonacciGenerator(...).Take(count);

Some middle ground can be probably achieved if you use Range instead:

Enumerable.Range(0, count) //extend this
          .Fibonacci(3, 4);

This way one method should be enough in most cases. And you can either drop the Sequence class altogether or leave it as a pretty wrapper around Enumerable.Range or similar loop abstraction:

Sequence.Create(10)      //returns basic 0..9 sequence (IEnumerable<int>)
        .Fibonacci(3, 4) //returns Fibonacci sequence, using initial sequence as loop

I am still not convinced that all this boiler plate code is worth the effort though. But oh well. :)

P.S. I think your Fibonacci sequence implementation is a bit unconventional. There is no "step" in classical implementation:

every number after the first two is the sum of the two preceding ones

So if I were to see Fibonacci(3, 4) in code, I would assume, that 3 and 4 are the first two numbers of the sequence, and that it will go as 3, 4, 7, 11 and not as 3, 3, 7, 10, 17 (3+3 =/= 7).

\$\endgroup\$
  • \$\begingroup\$ The problem with Take or Range is that you can use Select instead and get an infinite sequence. I wanted to separate the generator method from getting the items so that you cannot create such infinite sequences. Sequence.Generate(10).Generate(...) I admit, I should try to fix it. \$\endgroup\$ – t3chb0t Nov 14 '16 at 13:48
  • \$\begingroup\$ I think your Fibonacci sequence implementation is a bit unconventional - I take this as a compliment ;-D Originally I used the Fibonacci sequence for delay in my Retry implementation so I needed the first step as a measure for fine tuning. \$\endgroup\$ – t3chb0t Nov 14 '16 at 13:51
  • \$\begingroup\$ With Range - it depends on generator implementation. If generator loops over initial collection, then it will only be infinite if initial collection is infinite. For Take, yes, there is a risk. But you could just pass count as parameter to generator function. \$\endgroup\$ – Nikita B Nov 14 '16 at 14:06
  • \$\begingroup\$ I think I'll stick to the inheritance version. Make the class non-ienumerable and add a Take method to it so that the constructor is count-free. \$\endgroup\$ – t3chb0t Nov 14 '16 at 15:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.