HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Sequence generators not only for potatoes but also for apples and oranges

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
potatoesbutorangessequencealsoforonlyandnotgenerators

Problem

The last question about sequence generators Growing potatoes in delayed sequences was only about potatoes. I thougt why not make it work with apples and oranges too so I made it generic.

The base class got a new name and a generic argument and now looks like this:

public abstract class GeneratedSequence : IEnumerable
{
    protected GeneratedSequence(int count) { Count = count; }

    public int Count { get; }

    public IEnumerator GetEnumerator() => Generate().Take(Count).GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    protected abstract IEnumerable Generate();
}


The RegularSequence has been upgraded and is now generic too:

public class RegularSequence : GeneratedSequence
{
    private readonly T _value;
    public RegularSequence(T value, int count) : base(count) { _value = value; }
    protected override IEnumerable Generate()
    {
        while (true) yield return _value;
    }
}


The biggest change undergone however the FibonnaciSequence. Generics don't support arithmetic operations so I needed to add a lambda for the sum. Now it looks like this:

public class FibonacciSequence : GeneratedSequence
{
    private T _preview;
    private T _current;
    private readonly Func _sum;

    public FibonacciSequence(T firstTwo, T firstStep, int count, Func sum) : base(count)
    {
        _sum = sum;
        _preview = firstTwo;
        _current = _sum(_preview, firstStep);
    }   

    protected override IEnumerable Generate()
    {
        yield return _preview;
        yield return _preview;
        yield return _current;

        while (true)
        {
            var newCurrent = _sum(_preview, _current);
            yield return newCurrent;
            _preview = _current;
            _current = newCurrent;
        }
    }
}


To simplify the creation process I also added a new FibonacciSequenceFactory:

```
public class FibonacciSequenceFactory
{
public static FibonacciSequence Create

Solution

A slightly different approach is to avoid the sublclassing and instead feed the generic DelaySequence class with a generator function. It makes it more flexible.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace CR146856
{
  public class MyDelaySequence : IEnumerable
  {
    Func> m_generator;
    int m_count = 0;

    /// 
    /// A contructor for a plain sequence of regular delays
    /// 
    /// Number of retries.
    /// The delay after each retry.
    public MyDelaySequence(int count, T delay)
    {
      m_count = count;
      m_generator = () =>
      {
        return Enumerable.Range(1, count).Select(i => delay);
      };
    }

    /// 
    /// Constructor for a custom delay generator.
    /// 
    /// Number of retries.
    /// A custom generator of delays between retries.
    public MyDelaySequence(int count, Func> generator)
    {
      m_count = count;
      m_generator = generator;
    }

    public IEnumerator GetEnumerator()
    {
      return m_generator().Take(m_count).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
      return GetEnumerator();
    }
  }

  class Program
  {
    static IEnumerable GeometricDelayGenerator()
    {
      int delay = 1000;
      while (true)
      {
        yield return delay;
        delay += delay;
      }
    }

    static IEnumerable FibonacciIntDelayGenerator()
    {
      int first = 1000;

      yield return first;
      yield return first;

      int second = first + 3000;
      yield return second;

      while (true)
      {
        yield return first + second;
        int tmp = first;
        first = second;
        second = first + tmp;
      }
    }

    static IEnumerable FibonacciTimeSpanDelayGenerator()
    {
      TimeSpan first = TimeSpan.FromSeconds(1);

      yield return first;
      yield return first;

      TimeSpan second = first + TimeSpan.FromSeconds(3);
      yield return second;

      while (true)
      {
        yield return first + second;
        TimeSpan tmp = first;
        first = second;
        second = first + tmp;
      }
    }

    static void Main(string[] args)
    {
      try
      {
        TimeSpan seqDelay = TimeSpan.FromSeconds(1);
        int count = 7;

        foreach (var ts in new MyDelaySequence(count, FibonacciTimeSpanDelayGenerator))
        {
          Console.WriteLine(ts);
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine($"ERROR: {ex.Message}");
      }

      Console.WriteLine("END");
      Console.ReadLine();
    }
  }
}


That said I tend to think you overdo a rather simple construct:

foreach (var delay in FibonacciIntDelayGenerator().Take(count))
    {
      Console.WriteLine($"If service not responding wait in {delay} ms and retry.");
    }


EDIT:

In case of a reusable library of functions I would do it the static way:

public static class MyDelayGenerators
  {
    public static IEnumerable DelayGenerator(int retries, T initialDelay, Func offsetFunc)
    {
      T delay = initialDelay;
      for (int i = 0; i  GeometricDelayGenerator(int retries, T initialDelay, Func sum)
    {
      return DelayGenerator(retries, initialDelay, sum);
    }

    public static IEnumerable GeometricDelayGenerator(int retries, int initialDelay)
    {
      return DelayGenerator(retries, initialDelay, (d) => d + d);
    }

    public static IEnumerable GeometricDelayGenerator(int retries, TimeSpan initialDelay)
    {
      return DelayGenerator(retries, initialDelay, (d) => d + d);
    }

    public static IEnumerable RegularDelayGenerator(int retries, int delay)
    {
      for (int i = 0; i  RegularDelayGenerator(int retries, TimeSpan delay)
    {
      for (int i = 0; i  FibonacciDelayGenerator(int retries, T first, T firstOffset, Func sum)
    {
      yield return first;
      yield return first;

      T second = sum(first, firstOffset);
      yield return second;

      retries -= 3;
      for (int i = 0; i  FibonacciDelayGenerator(int retries, int first = 1000, int firstOffset = 1000)
    {
      return FibonacciDelayGenerator(retries, first, firstOffset, (f, s) =>
      {
        int tmp = f;
        f = s;
        s = f + tmp;
        return s;
      });
    }

    public static IEnumerable FibonacciDelayGenerator(int retries, TimeSpan first, TimeSpan firstOffset)
    {
      return FibonacciDelayGenerator(retries, first, firstOffset, (f, s) =>
      {
        TimeSpan tmp = f;
        f = s;
        s = f + tmp;
        return s;
      });
    }
  }


It has a number of predefined functions and a set of generic as well to be use on the fly.

Code Snippets

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace CR146856
{
  public class MyDelaySequence<T> : IEnumerable<T>
  {
    Func<IEnumerable<T>> m_generator;
    int m_count = 0;

    /// <summary>
    /// A contructor for a plain sequence of regular delays
    /// </summary>
    /// <param name="count">Number of retries.</param>
    /// <param name="delay">The delay after each retry.</param>
    public MyDelaySequence(int count, T delay)
    {
      m_count = count;
      m_generator = () =>
      {
        return Enumerable.Range(1, count).Select(i => delay);
      };
    }

    /// <summary>
    /// Constructor for a custom delay generator.
    /// </summary>
    /// <param name="count">Number of retries.</param>
    /// <param name="generator">A custom generator of delays between retries.</param>
    public MyDelaySequence(int count, Func<IEnumerable<T>> generator)
    {
      m_count = count;
      m_generator = generator;
    }

    public IEnumerator<T> GetEnumerator()
    {
      return m_generator().Take(m_count).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
      return GetEnumerator();
    }
  }

  class Program
  {
    static IEnumerable<int> GeometricDelayGenerator()
    {
      int delay = 1000;
      while (true)
      {
        yield return delay;
        delay += delay;
      }
    }

    static IEnumerable<int> FibonacciIntDelayGenerator()
    {
      int first = 1000;

      yield return first;
      yield return first;

      int second = first + 3000;
      yield return second;


      while (true)
      {
        yield return first + second;
        int tmp = first;
        first = second;
        second = first + tmp;
      }
    }

    static IEnumerable<TimeSpan> FibonacciTimeSpanDelayGenerator()
    {
      TimeSpan first = TimeSpan.FromSeconds(1);

      yield return first;
      yield return first;

      TimeSpan second = first + TimeSpan.FromSeconds(3);
      yield return second;


      while (true)
      {
        yield return first + second;
        TimeSpan tmp = first;
        first = second;
        second = first + tmp;
      }
    }

    static void Main(string[] args)
    {
      try
      {
        TimeSpan seqDelay = TimeSpan.FromSeconds(1);
        int count = 7;

        foreach (var ts in new MyDelaySequence<TimeSpan>(count, FibonacciTimeSpanDelayGenerator))
        {
          Console.WriteLine(ts);
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine($"ERROR: {ex.Message}");
      }

      Console.WriteLine("END");
      Console.ReadLine();
    }
  }
}
foreach (var delay in FibonacciIntDelayGenerator().Take(count))
    {
      Console.WriteLine($"If service not responding wait in {delay} ms and retry.");
    }
public static class MyDelayGenerators
  {
    public static IEnumerable<T> DelayGenerator<T>(int retries, T initialDelay, Func<T, T> offsetFunc)
    {
      T delay = initialDelay;
      for (int i = 0; i < retries; i++)
      {
        yield return delay;
        delay = offsetFunc(delay);
      }
    }

    public static IEnumerable<T> GeometricDelayGenerator<T>(int retries, T initialDelay, Func<T, T> sum)
    {
      return DelayGenerator(retries, initialDelay, sum);
    }

    public static IEnumerable<int> GeometricDelayGenerator(int retries, int initialDelay)
    {
      return DelayGenerator(retries, initialDelay, (d) => d + d);
    }

    public static IEnumerable<TimeSpan> GeometricDelayGenerator(int retries, TimeSpan initialDelay)
    {
      return DelayGenerator(retries, initialDelay, (d) => d + d);
    }

    public static IEnumerable<int> RegularDelayGenerator(int retries, int delay)
    {
      for (int i = 0; i < retries; i++)
      {
        yield return delay;
      }
    }

    public static IEnumerable<TimeSpan> RegularDelayGenerator(int retries, TimeSpan delay)
    {
      for (int i = 0; i < retries; i++)
      {
        yield return delay;
      }
    }

    public static IEnumerable<T> FibonacciDelayGenerator<T>(int retries, T first, T firstOffset, Func<T, T, T> sum)
    {
      yield return first;
      yield return first;

      T second = sum(first, firstOffset);
      yield return second;

      retries -= 3;
      for (int i = 0; i < retries; i++)
      {
        T tmp = sum(first, second);
        yield return tmp;
        first = second;
        second = tmp;
      }
    }

    public static IEnumerable<int> FibonacciDelayGenerator(int retries, int first = 1000, int firstOffset = 1000)
    {
      return FibonacciDelayGenerator(retries, first, firstOffset, (f, s) =>
      {
        int tmp = f;
        f = s;
        s = f + tmp;
        return s;
      });
    }

    public static IEnumerable<TimeSpan> FibonacciDelayGenerator(int retries, TimeSpan first, TimeSpan firstOffset)
    {
      return FibonacciDelayGenerator(retries, first, firstOffset, (f, s) =>
      {
        TimeSpan tmp = f;
        f = s;
        s = f + tmp;
        return s;
      });
    }
  }

Context

StackExchange Code Review Q#146856, answer score: 3

Revisions (0)

No revisions yet.