Skip to content

Instantly share code, notes, and snippets.

@klmr
Created May 31, 2011 16:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save klmr/1000783 to your computer and use it in GitHub Desktop.
Save klmr/1000783 to your computer and use it in GitHub Desktop.
LazyList in C#
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);
}
}
}
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