package jazz.util; /////////////////////////////////////////////////////////////////////////////// // // Generic linked lists // /////////////////////////////////////////////////////////////////////////////// public abstract class List<+T> { // The empty list public static nil: Nil<U> {U} = new Nil(); // List constructor public cons(head: T): Cons<T>; // List reverse public reverse(): alike<T>; // List concatenation public concat(l: alike<T>): alike<T>; // List iterator public map<U>(f: fun(T): U): alike<U>; // List folding public fold<U>(f0: U, f1: fun(T): U, f2: fun(T, U): U): U; // Length of a list public length(): int; // Element access public nth(n: int): T; // List formatting parameters. By default, lists are displayed in a // Lisp-like fashion, i.e., "(1 2 3 4)". public static dynamic lparen: String = "("; public static dynamic separator: String = " "; public static dynamic rparen: String = ")"; } /////////////////////////////////////////////////////////////////////////////// // // Implementation // /////////////////////////////////////////////////////////////////////////////// toString@List() = format("%s%s%s", lparen, fold("", f1, f2), rparen) { fun f1(t) = t.toString(); fun f2(t, u) = format("%a%s%s", t, separator, u); } cons@List(head) = new Cons(head = head, tail = this); map@Nil(f) = nil; map@Cons(f) = tail.map(f).cons(f(head)); concat@Nil(l) = l; concat@Cons(l) = tail.concat(l).cons(head); reverse@Nil<T>() = this; reverse@Cons<T>() = reverse(tail, nil.cons(head)) { fun reverse(l: List<T>, r: Cons<T>): Cons<T>; reverse(l@Nil, r) = r; reverse(l@Cons, r) = reverse(l.tail, r.cons(l.head)); } fold<U>@List<T>(f0, f1, f2) = fold(this) { fun fold(l: List<T>): U; fold(l@Nil) = f0; fold(l@Cons) = (l.tail instanceof Nil ? f1(l.head) : f2(l.head, fold(l.tail))); } length@Nil() = 0; length@Cons() = 1 + tail.length(); nth@Nil(n) = error("nth(nil)"); nth@Cons(n) = (n == 0 ? head : tail.nth(n-1));