October
7th,
2008
F# has a handy method called Unfold. Think of it as the logical opposite of an Aggregate function. Aggregates take a sequence of elements and convert them to a single element. An unfold method will take a single element and turn it into a (potentially infinite) sequence of elements.
The API is straight forward. It takes two parameters
- A seed/start value
- A function which takes in a seed value and returns either an Empty option to indicate the end of a sequence or a value of Type tuple with two arguments. The first is the next element in the sequence and the second is the next seed value passed into the function.
This is a very powerful function which allows you to quickly build all sorts of interesting functions. Lets look at a potential implementation of Enumerable.Range as an unfold expression.
public static IEnumerable<int> Range(int start, int count)
{
return Enumerable.Unfold(
start,
x => x - start < count
? Option.Create(Tuple.Create(x, x + 1))
: Option.Empty);
}
Not as efficient as the framework version of Range but a good mental exercise to get your head around using Unfold. Implementing this method in C# is fairly straight forward.
public static IEnumerable<TResult> Unfold<TSource, TResult>(
TSource state,
Func<TSource, Option<Tuple<TResult, TSource>>> func)
{
if (func == null)
{
throw new ArgumentNullException("func");
}
return UnfoldHelper(state, func);
}
private static IEnumerable<TResult> UnfoldHelper<TSource, TResult>(
TSource state,
Func<TSource, Option<Tuple<TResult, TSource>>> func)
{
do
{
var result = func(state);
if (!result.HasValue)
{
break;
}
yield return result.Value.First;
state = result.Value.Second;
} while (true);
}