For a start, here's your friendly neighborhood yield return in action (Fibonacci using IEnumerable):
using System; using System.Collections.Generic; using System.Linq; namespace Fibonacci { class MainClass { public static void Main (string[] args) { Console.WriteLine("Sans list. Lazy load stuff:"); int i = 0; foreach(int n in Fibonacci().Take(10)) { ++i; Console.WriteLine("Loading {0} {1}", i, n); } Console.WriteLine("\nPick the 20th fibonacci:"); Console.WriteLine("\n20th fibonacci: {0}", Fibonacci().Skip(20 - 1).Take(1).Single()); Console.WriteLine("\nEagerly load everything in list:"); i = 0; foreach(int n in Fibonacci().Take(10).ToList()) { ++i; Console.Write("\nEager loading {0} {1}", i, n); } } static IEnumerable<int> Fibonacci() { int a = 0, b = 1; for(;;) { Console.Write("Lazy"); yield return a; int n = a; a += b; b = n; } } }//class }
Contrast that to an old way, a lot of boilerplate code is needed (which maybe favorable to a PHB) :
using System; namespace Fib2 { using System.Linq; using FibLib; class MainClass { public static void Main(string[] args) { Console.WriteLine("Sans list. Lazy load stuff:"); int i = 0; var f = new Fibonacci(); foreach (int n in f.Take(10)) { ++i; Console.WriteLine("Loading {0} {1}", i, n); } Console.WriteLine("\nPick the 20th fibonacci:"); Console.WriteLine("\n20th fibonacci: {0}", f.Skip(20 - 1).Take(1).Single()); Console.WriteLine("\nEagerly load everything in list:"); i = 0; foreach (int n in f.Take(10).ToList()) { ++i; Console.Write("\nEager loading {0} {1}", i, n); } } } } namespace FibLib { class Fibonacci : System.Collections.Generic.IEnumerable<int> { System.Collections.Generic.IEnumerator<int> _f = new FibonacciIterator(); // implement IEnumerable<int>... System.Collections.Generic.IEnumerator<int> System.Collections.Generic.IEnumerable<int>.GetEnumerator() { _f.Reset(); return _f; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new Exception("This doesn't get executed. What's the use?"); return _f; } // ...implement IEnumerable class FibonacciIterator : System.Collections.Generic.IEnumerator<int> { int a = 0, b = 1; int System.Collections.Generic.IEnumerator<int>.Current { get { return b; } } object System.Collections.IEnumerator.Current { get { throw new Exception("This doesn't get executed. What's the use?"); return b; } } void System.IDisposable.Dispose() { } bool System.Collections.IEnumerator.MoveNext() { Console.Write("Lazy"); int n = a; a += b; b = n; return true; } void System.Collections.IEnumerator.Reset() { a = 0; b = 1; } } }//class Fibonacci }
Code A has 55 lines of code, while code B has 108 lines
yield return saves the day! :-)
Output of both approach:
Sans list. Lazy load stuff: LazyLoading 1 0 LazyLoading 2 1 LazyLoading 3 1 LazyLoading 4 2 LazyLoading 5 3 LazyLoading 6 5 LazyLoading 7 8 LazyLoading 8 13 LazyLoading 9 21 LazyLoading 10 34 Pick the 20th fibonacci: LazyLazyLazyLazyLazyLazyLazyLazyLazyLazyLazyLazyLazyLazyLazyLazyLazyLazyLazyLazy 20th fibonacci: 4181 Eagerly load everything in list: LazyLazyLazyLazyLazyLazyLazyLazyLazyLazy Eager loading 1 0 Eager loading 2 1 Eager loading 3 1 Eager loading 4 2 Eager loading 5 3 Eager loading 6 5 Eager loading 7 8 Eager loading 8 13 Eager loading 9 21 Eager loading 10 34
This is the SQL and eager-loading approach: http://www.ienablemuch.com/2010/09/fibonacci-using-sql.html