-
Notifications
You must be signed in to change notification settings - Fork 15
Cycle
The Cycle()
methods endlessly repeat an enumerable sequence. It comes in two flavours:
public static IEnumerable<T> Cycle<T>(this IEnumerable<T> collection)
public static IEnumerable<T> Cycle<T>(params T[] collection)
In both cases, Cycle()
will return an IEnumerable<T>
which will yield each element of the collection in turn, before looping back to the start and re-yielding them. The items are cached as they are yielded, therefore subsequence re-yields do not re-enumerate the enumerable.
Let's say we have a need to provide a method that endlessly repeats a British traffic light sequence of green, amber, red, red-and-amber and back to green.
We'll define some basic types first:
public enum LightState
{
Off,
On
}
public struct Lights
{
public LightState Red { get; }
public LightState Amber { get; }
public LightState Green { get; }
public Lights(LightState red, LightState amber, LightState green)
=> (Red, Amber, Green) = (red, amber, green);
}
Now let's consider a fairly typical imperative implementation:
private enum LightsStage
{
Green,
Amber,
Red,
RedAmber
}
public static IEnumerable<Lights> SequenceTrafficLightImperative()
{
var state = LightsStage.Green;
while (true)
{
switch (state)
{
case LightsStage.Green:
yield return new Lights(LightState.Off, LightState.Off, LightState.On);
state = LightsStage.Amber;
break;
case LightsStage.Amber:
yield return new Lights(LightState.Off, LightState.On, LightState.Off);
state = LightsStage.Red;
break;
case LightsStage.Red:
yield return new Lights(LightState.On, LightState.Off, LightState.Off);
state = LightsStage.RedAmber;
break;
case LightsStage.RedAmber:
yield return new Lights(LightState.On, LightState.On, LightState.Off);
state = LightsStage.Green;
break;
}
}
}
It's got the things one would typically expect of an imperative approach: a while
loop, a switch
statement and a constantly mutating variable, state
. It's also, as is typical of imperative programming, long.
The Cycle
method let's us reduce this code considerably:
public static IEnumerable<Lights> SequenceTrafficLightDeclarative()
=> new List<Lights> {
new Lights(LightState.Off, LightState.Off, LightState.On),
new Lights(LightState.Off, LightState.On, LightState.Off),
new Lights(LightState.On, LightState.Off, LightState.Off),
new Lights(LightState.On, LightState.On, LightState.Off)
}.Cycle();
We simply declare a sequence list and use Cycle
to repeat it. We have reduced the whole thing to one (admittedly long, if we really did put it on one line) line of code and so we can even use the expression body notation (=>
) for it.
Action
/Func
conversionsCycle
methods- Converting between
Action
andFunc
- Extension methods for existing types that use
Option<T>
- Indexed enumerations
IEnumerable<T>
cons- Option-based parsers
- Partial function applications
- Pattern matching
- Pipe Operators
- Typed lambdas
Any
Either<TLeft,TRight>
None
Option<T>
Success<T>
Union<T1,T2>
Union<T1,T2,T3>
Union<T1,T2,T3,T4>
Unit
ValueOrError