Created
May 31, 2011 16:15
-
-
Save klmr/1000783 to your computer and use it in GitHub Desktop.
LazyList in C#
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; | |
namespace LazyList { | |
public sealed class Fit : Exception { | |
public Fit(string message) : base(message) { } | |
} | |
public static class List { | |
public static Nil<T> Nil<T>() { | |
return List<T>.Nil(); | |
} | |
public static Cons<T> Cons<T>(T car, List<T> cdr) { | |
return List<T>.Cons(car, cdr); | |
} | |
public static Thunk<T> Thunk<T>(Func<List<T>> generator) { | |
return List<T>.Thunk(generator); | |
} | |
public static List<T> ZipWith<T>(Func<T, T, T> func, List<T> a, List<T> b) { | |
return Thunk(() => a.ZipWith(func, b)); | |
} | |
public static List<T> Create<T>(params T[] values) { | |
List<T> ret = Nil<T>(); | |
for (int i = values.Length - 1; i >= 0; i--) { | |
ret = Cons(values[i], ret); | |
} | |
return ret; | |
} | |
} | |
public abstract class List<T> { | |
private static readonly Nil<T> __nil = new Nil<T>(); | |
public static Nil<T> Nil() { | |
return __nil; | |
} | |
public static Cons<T> Cons(T car, List<T> cdr) { | |
return new Cons<T>(car, cdr); | |
} | |
public static Thunk<T> Thunk(Func<List<T>> generator) { | |
return new Thunk<T>(generator); | |
} | |
protected Fit Fit(string message) { | |
return new Fit(message); | |
} | |
public abstract List<T> Force(); | |
public abstract bool IsEmpty { get; } | |
public abstract T Head { get; } | |
public abstract List<T> Tail { get; } | |
public abstract List<T> ZipWith(Func<T, T, T> func, List<T> other); | |
public abstract List<T> Take(int n); | |
public abstract void Print(); | |
public abstract List<T> Concat(List<T> other); | |
} | |
public sealed class Nil<T> : List<T> { | |
public Nil() { } | |
public override string ToString() { | |
return "[]"; | |
} | |
public override List<T> Force() { | |
return this; | |
} | |
public override bool IsEmpty { | |
get { return true; } | |
} | |
public override T Head { | |
get { throw Fit("Trying to car from empty list"); } | |
} | |
public override List<T> Tail { | |
get { throw Fit("Trying to cdr from empty list"); } | |
} | |
public override List<T> ZipWith(Func<T, T, T> func, List<T> other) { | |
return Nil(); | |
} | |
public override List<T> Take(int n) { | |
if (n != 0) | |
throw Fit("Trying to take from empty list"); | |
return Nil(); | |
} | |
public override void Print() { | |
Console.WriteLine("[]"); | |
} | |
public override List<T> Concat(List<T> other) { | |
return other; | |
} | |
} | |
public sealed class Cons<T> : List<T> { | |
private readonly T car; | |
private readonly List<T> cdr; | |
public Cons(T car, List<T> cdr) { | |
this.car = car; | |
this.cdr = cdr; | |
} | |
public override string ToString() { | |
return string.Format("{0} : {1}", car, cdr); | |
} | |
public override List<T> Force() { | |
return this; | |
} | |
public override bool IsEmpty { | |
get { return false; } | |
} | |
public override T Head { | |
get { return car; } | |
} | |
public override List<T> Tail { | |
get { return cdr; } | |
} | |
public override List<T> ZipWith(Func<T, T, T> func, List<T> other) { | |
return Thunk(() => { | |
if (other.IsEmpty) | |
return Nil(); | |
return Cons(func(Head, other.Head), Tail.ZipWith(func, other.Tail)); | |
}); | |
} | |
public override List<T> Take(int n) { | |
return Thunk(() => { | |
if (n == 0) | |
return Nil(); | |
return Cons(Head, Tail.Take(n - 1)); | |
}); | |
} | |
public override void Print() { | |
Console.Write("{0} : ", Head); | |
Tail.Print(); | |
} | |
public override List<T> Concat(List<T> other) { | |
return Cons(Head, Tail.Concat(other)); | |
} | |
} | |
public sealed class Thunk<T> : List<T> { | |
private List<T> actual; | |
private readonly Func<List<T>> generator; | |
public Thunk(Func<List<T>> generator) { | |
this.generator = generator; | |
this.actual = this; | |
} | |
public override string ToString() { | |
return "{Thunk}"; | |
} | |
public override List<T> Force() { | |
var generated = generator(); | |
actual = generated.Force(); // May be recursive. | |
return actual; | |
} | |
private List<T> ForcedSelf() { | |
return actual.Force(); | |
} | |
public override bool IsEmpty { | |
get { return ForcedSelf().IsEmpty; } | |
} | |
public override T Head { | |
get { return ForcedSelf().Head; } | |
} | |
public override List<T> Tail { | |
get { return ForcedSelf().Tail; } | |
} | |
public override List<T> ZipWith(Func<T, T, T> func, List<T> other) { | |
return ForcedSelf().ZipWith(func, other); | |
} | |
public override List<T> Take(int n) { | |
return ForcedSelf().Take(n); | |
} | |
public override void Print() { | |
ForcedSelf().Print(); | |
} | |
public override List<T> Concat(List<T> other) { | |
return ForcedSelf().Concat(other); | |
} | |
} | |
} |
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; | |
namespace LazyList { | |
class MainClass { | |
static List<int> Fibonacci() { | |
List<int> fib = null; | |
fib = List.Cons(0, List.Cons(1, | |
List.ZipWith( | |
(a, b) => a + b, | |
List.Thunk(() => fib), | |
List.Thunk(() => fib.Tail)))); | |
return fib; | |
//List<int> fib = List.Cons(0, List.Cons(1, List.Nil<int>())); | |
//fib = fib.Concat(fib.ZipWith((a, b) => a + b, fib.Tail)); | |
//return fib; | |
} | |
static List<int> CreateThunkForTesting() { | |
var lst = List.Create(1, 2, 3, 4, 5); | |
return List.Thunk(() => { | |
Console.Write("{Evaluating thunk} "); | |
return lst; | |
}); | |
} | |
static void TestPrint() { | |
Console.WriteLine("== TestPrint =="); | |
var lst = CreateThunkForTesting(); | |
Console.Write("List: "); | |
Console.WriteLine(lst); | |
Console.Write("List self: "); | |
lst.Print(); | |
lst.Print(); | |
} | |
static void TestZip() { | |
Console.WriteLine("== TestZip =="); | |
var lst1 = CreateThunkForTesting(); | |
var lst2 = CreateThunkForTesting(); | |
var result = List.ZipWith((a, b) => a + b, lst1, lst2); | |
result.Print(); | |
} | |
static void TestTake() { | |
Console.WriteLine("== TestTake =="); | |
var lst = CreateThunkForTesting(); | |
lst.Take(4).Print(); | |
lst.Take(3).Print(); | |
lst.Take(4).Take(2).Print(); | |
lst.Take(1).Take(1).Print(); | |
lst.Take(0).Print(); | |
} | |
static void TestConcat() { | |
Console.WriteLine("== TestConcat =="); | |
var lst1 = CreateThunkForTesting(); | |
var lst2 = CreateThunkForTesting(); | |
var result = lst1.Concat(lst2); | |
result.Print(); | |
} | |
static void TestFib() { | |
Console.WriteLine("== TestFib =="); | |
var fibs = Fibonacci(); | |
Console.WriteLine(fibs); | |
fibs.Take(45).Print(); | |
} | |
public static void Main(string[] args) { | |
TestPrint(); | |
TestZip(); | |
TestTake(); | |
TestConcat(); | |
TestFib(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment