Last active
January 1, 2024 22:12
-
-
Save mrange/b501838e5edcec319b7881de06ae5a2a to your computer and use it in GitHub Desktop.
Examples IEnumerable implementing a fibonacci series
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Example on the "easy" way to generate the fibonacci sequence | |
// https://en.wikipedia.org/wiki/Fibonacci_number | |
// Take the first 20 fibonacci numbers and print them | |
// the "easy" way. | |
foreach(var n in Fibonacci().Take(20)) | |
{ | |
Console.WriteLine(n); | |
} | |
// How to implement the fibonacci sequence as IEnumerable | |
// the "easy" way | |
// IEnumerable is an abstraction of a sequence | |
// in this case a sequence of fibonacci numbers | |
static IEnumerable<long> Fibonacci() | |
{ | |
long previous = 1; | |
long current = 0; | |
// This looks like an infinite loop which normally | |
// would either cause program not to terminate | |
// or run out of memory | |
// However, IEnumerable is a lazy enumerator | |
// meaning it generates only 1 element at a time | |
// and waiting until asked by the consumer for | |
// another value | |
while(true) | |
{ | |
// Compute new current by adding the previous | |
// and the current value together | |
// Store the previous current value as previous | |
var tmp = current; | |
current = tmp+previous; | |
previous = tmp; | |
// yield return current value as next element | |
// in the IEnumerable, yield implies the execution | |
// stops until the consumer asks for a new value | |
yield return current; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
// Example on the "hard" way to generate the fibonacci sequence | |
// https://en.wikipedia.org/wiki/Fibonacci_number | |
// Take the first 20 fibonacci numbers and print them | |
// the "hard" way. | |
// Normally we do foreach but this what the compiler | |
// generates for you | |
// e is an IEnumerator, as that is IDisposable we should | |
// use using to ensure the Dispose is always executed | |
using var e = new FibonacciEnumerable().Take(20).GetEnumerator(); | |
// Loop as long as MoveNext returns true | |
while (e.MoveNext()) | |
{ | |
// As MoveNext returned true e.Current has a valid value | |
Console.WriteLine(e.Current); | |
} | |
// How to implement the fibonacci sequence as IEnumerable | |
// the "hard" way | |
// IEnumerable is an abstraction of a sequence | |
// in this case a sequence of fibonacci numbers | |
class FibonacciEnumerable : IEnumerable<long> | |
{ | |
// Create an enumerator and return it | |
public IEnumerator<long> GetEnumerator() => | |
new FibonacciEnumerator(); | |
// IEnumerable<T> extends IEnumerable which is the | |
// .NET 1.0 (about year 2000) interface so we need to | |
// implement a that GetEnumerator as well. Just call the | |
// the actual GetEnumerator implementation | |
// It looks recursive but as this is a private | |
// implementation of an interface it calls the GetEnumerator | |
// above | |
IEnumerator IEnumerable.GetEnumerator() => | |
GetEnumerator(); | |
} | |
// The enumerator does the actual work | |
class FibonacciEnumerator : IEnumerator<long> | |
{ | |
// To compute the fibonacci sequence we track | |
// the previous value and the current value | |
long _previous = 1; | |
long _current = 0; | |
// Current is used to query the current value of the | |
// enumerator. You may only call Current if | |
// MoveNext returned true | |
public long Current => _current; | |
// MoveNext typical does most of the work | |
public bool MoveNext() | |
{ | |
// Compute new current by adding the previous | |
// and the current value together | |
// Store the previous current value as previous | |
var tmp = _current; | |
_current = tmp + _previous; | |
_previous = tmp; | |
return true; | |
} | |
// Reset is used to restore the enumerator to the initial | |
// state before the iteration. Not commonly used IMHO | |
public void Reset() | |
{ | |
_previous = 1; | |
_current = 0; | |
} | |
// Some enumerators iterate over files and network resources | |
// We therefore need a way to dispose them when the iteration | |
// is done in order to avoid memory and resource leaks | |
public void Dispose() | |
{ | |
// In this case no specific dispose is needed | |
} | |
// IEnumerator<T> extends IEnumerator which is the | |
// .NET 1.0 (about year 2000) interface so we need to | |
// implement that Current as well. Just call the | |
// the actual Current implementation | |
// It looks recursive but as this is a private | |
// implementation of an interface it calls the Current | |
// above | |
object IEnumerator.Current => Current; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment