/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * JavaWorld Library, Copyright 2011 Bryan Chadwick * * * * FILE: ./universe/List.java * * * * This file is part of JavaWorld. * * * * JavaWorld is free software: you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation, either version * * 3 of the License, or (at your option) any later version. * * * * JavaWorld is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with JavaWorld. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package universe; import java.util.Iterator; import java.util.NoSuchElementException; /** Represents Lisp style Lists. List.create(...) should be used to create lists, and * push/pop/top/etc... to pull them apart. All methods are functional, meaning a * given list is an immutable data type: methods return a new list, they do not * change the old one. The methods can be used to write beautiful recursive * functions :) * * The inner classes (Build, Comp, GComp, Fold, * Map, Pred, and Zip) are Function Classes that * allow you to implement needed functionalities over basic traversals/iteration. * * This implementation has been updated to eliminate stack usage, which allows * you (the programmer) to create HUGE lists without any problems ;) * */ public abstract class List implements java.lang.Iterable, java.io.Serializable{ private static final long serialVersionUID = 1; static class TestB extends List.Build{ public Integer build(int i){ return (int)(Math.random()*100); } } static class TestC extends List.Comp{ public boolean comp(Integer i, Integer j){ return i < j; } } public static void main(String[] args){ List lst = List.buildlist(new TestB(), 3000); Comp c = new TestC(); long st = System.currentTimeMillis(); for(int i = 0; i < 20; i++)lst.mergeSort(c); System.out.println(" Merge: "+(System.currentTimeMillis()-st)); st = System.currentTimeMillis(); for(int i = 0; i < 20; i++)lst.sort(c); System.out.println(" Insert: "+(System.currentTimeMillis()-st)); } /** Compute a String from a List (Visitor) */ public static abstract class Stringer{ public abstract String toString(X f, List r); } /** Select Elements of a List */ public static abstract class Pred{ /** Is this predicate true for the given X? */ public abstract boolean huh(X x); /** Return a Predicate that is the negation of this one */ public Pred negate(){ return new Negate(this); } private static class Negate extends Pred{ Pred p; Negate(Pred pp){ this.p = pp; } public boolean huh(X x){ return !this.p.huh(x); } } } /** Compare two List Elements of the same type */ public static abstract class Comp extends GComp{} /** (General) Compare two List Elements of possibly different types * (true is "LessThan" for sort(), but "Equal" for same(...)) */ public static abstract class GComp{ /** Compare these two Xs, returning true/false */ public abstract boolean comp(X x, Y y); //private GComp that; /** Return a general comparator with flipped comparision (and types) */ public GComp flip(){ return new Flip(this); } private class Flip extends GComp{ Flip(GComp p){ this.par = p; } GComp par; public boolean comp(Y y, X x){ return this.par.comp(x, y); } } /** Return a Predicate that uses the comparator (later) using the given X as * its first argument */ public Pred curry(Y y){ return new Curry(this,y); } /** Return a (reversed) Predicate that uses the comparator (later) using the given Y as * its second argument */ public Pred revCurry(X x){ return new RCurry(this,x); } private static class Curry extends Pred{ Y y; GComp c; Curry(GComp cc, Y yy){ this.c = cc; this.y = yy; } public boolean huh(X x){ return this.c.comp(x, this.y); } } private static class RCurry extends Pred{ X x; GComp c; RCurry(GComp cc, X xx){ this.c = cc; this.x = xx; } public boolean huh(Y y){ return this.c.comp(this.x, y); } } } /** Apply a function to each element of the list */ public static abstract class Map{ public abstract Y map(X x); } /** Fold the List into a single Value */ public static abstract class Fold{ public abstract Y fold(X x, Y y); } /** Zip two Lists into a single Value */ public static abstract class Zip{ public abstract Z zip(X x, Y y); } /** Build a List from the sequence of integers [0..len-1] */ public static abstract class Build{ public abstract X build(int i); } /** Equals Comparator */ private class EqualComp extends Comp{ public boolean comp(X x, X y){ return x.equals(y); } } /** Create an Empty List */ public static List create(){ return new Empty(); } /** Create a List from a single argument */ public static List create(X x){ return List.create().push(x); } /** Create a List from an array/variable arguments */ public static List create(X ... xa){ return create(xa,0); } /** Create a List from a fixed array, starting at index 'i' */ public static List create(X[] xa, int i){ List res = List.create(); for(;i < xa.length;i++)res = res.push(xa[i]); return res.reverse(); } /** Create a List from an Iterable... */ public static List create(java.lang.Iterable xs){ List res = List.create(); for(X x:xs)res = res.push(x); return res.reverse(); } private final int len; /** Default Constructor */ public List(int l){ this.len = l; } /** Push a X on the front of this List */ public List push(X x){ return new Cons(x,this); } /** Push all Elements of the given List on the front of this List */ public List push(List xs){ return xs.append(this); } /** Reverse this List */ public List reverse(){ return reverse(length()); } /** Reverse the first i elements of this List */ public List reverse(int i){ List front = List.create(), back = this; while(!back.isEmpty() && i-- > 0){ X x = back.top(); back = back.pop(); front = front.push(x); } return front; } /** Canonical ToString */ public String toString(){ return toString(" ",""); } /** Return the Index of the given element */ public int index(X x){ return index(new EqualComp().curry(x)); } /** Return the Index of the first match */ public int index(List.Pred p){ int i = 0; for(X x:this){ if(p.huh(x))return i; i++; } return -1; } /** Is the given list have the same elements as this list? */ public boolean same(List l){ return containsAll(l) && l.containsAll(this); } /** Is the given list have the same elements as this list using the given comparer? */ public boolean same(List l, Comp c){ return sameG(l,c); } /** Is the given list have the same elements as this list using the given comparer? */ public boolean sameG(List l, GComp c){ return containsAllG(l,c) && l.containsAllG(this,c.flip()); } /** Return this List without the first k Elements */ public List pop(int k){ return (k == 0)?this:pop().pop(k-1); } /** Append another List to the end of this List */ public List append(List l){ List rev = reverse(); return l.revpush(rev); } /** Append an element to the end of this List */ public List append(X x){ return append(List.create(x)); } /** Return the first Element of this List */ public abstract X top(); /** Return this List without the first Element */ public abstract List pop(); /** Is this List Empty? */ public abstract boolean isEmpty(); /** Does this Predicate match any element in this List? */ public boolean ormap(final List.Pred p){ for(X x:this) if(p.huh(x))return true; return false; } /** Does this Predicate match all the elements in this List? */ public boolean andmap(final List.Pred p){ return !ormap(p.negate()); } /** Does the given X occur in this List? */ public boolean contains(X x){ return contains(new EqualComp().curry(x)); } /** Does this Predicate match anything in this List? */ public boolean contains(final List.Pred p){ return ormap(p); } /** Does the given X occur in this List? */ public boolean containsG(Y y, GComp c){ return contains(c.curry(y)); } private static class AnyP extends List.Pred{ public AnyP(List ll, GComp cc){ this.l = ll; this.c = cc; } List l; GComp c; public boolean huh(X x){ return this.l.contains(this.c.revCurry(x)); } } /** Does this List contain any of the given List's Elements? */ public boolean containsAny(List l){ return containsAny(l, new EqualComp()); } /** Does this List contain any of the given List's Elements using the given comparer? */ public boolean containsAny(final List l, final Comp c) { return this.ormap(new AnyP(l,c)); } /** Does this List contain any of the given List's Elements using the given comparer? */ public boolean containsAnyG(final List l, final GComp c){ return this.ormap(new AnyP(l,c)); } /** Does this List contain all of the given List's Elements? */ public boolean containsAll(List l){ return containsAll(l, new EqualComp()); } /** Does this List contain all of the given List's Elements using the given comparer? */ public boolean containsAll(final List l, final Comp c){ return l.andmap(new AnyP(this,c)); } /** Does this List contain all of the given List's Elements using the given comparer? */ public boolean containsAllG(final List l, final GComp c){ return l.andmap(new AnyP(this,c.flip())); } /** Return the given X, throws a RuntimeException if not there */ public X find(X x){ return find(new EqualComp().curry(x)); } /** Return the first matching X, throws a RuntimeException if not there */ public X find(List.Pred p){ for(X x:this) if(p.huh(x))return x; throw new RuntimeException("No Match Found: "+p); } /** Return the given X, throws a RuntimeException if not there */ public X findG(Y y, GComp c){ return find(c.curry(y)); } /** Remove the first occurence of the given X */ public List remove(X x){ return remove(new EqualComp().curry(x)); } /** Remove the first occurence of the given X */ public List removeG(Y y, GComp c){ return remove(c.curry(y)); } /** Push the reverse of the given list on the front of this one */ private List revpush(List l){ List ret = this; for(X x:l)ret = ret.push(x); return ret; } /** Remove the first matching X */ public List remove(List.Pred p){ List front = List.create(), back = this; while(!back.isEmpty()){ X x = back.top(); back = back.pop(); if(p.huh(x))return back.revpush(front); front = front.push(x); } return front.reverse(); } /** The Length of This List */ public int length(){ return this.len; } /** Lookup the i^th item in this List */ public X lookup(int i){ List l = this; while(i-- > 0)l = l.pop(); return l.top(); } /** To String, with a separator and prefix */ public String toString(String sep, String pre){ String ret = ""; boolean first = true; for(X x:this){ if(!first)ret += sep; else first = false; ret += pre+x; } return ret; } /** To String using a Stringer (Visitor) */ public String toString(Stringer s){ String ret = ""; List lst = this; while(!lst.isEmpty()){ ret += s.toString(lst.top(),lst.pop()); lst = lst.pop(); } return ret; } /** Filter out all the same Elements */ public List filterout(X x){ return filterout(new EqualComp().curry(x)); } /** Filter out all the matching Elements */ public List filterout(final List.Pred p){ return filter(p.negate()); } /** Filter out all the non-same Elements */ public List filter(X x){ return filter(new EqualComp().curry(x)); } /** Filter out all the non-matching Elements */ public List filter(List.Pred p){ List keep = List.create(); for(X x:this) if(p.huh(x))keep = keep.push(x); return keep.reverse(); } /** Count the elements that match the given Predicate */ public int count(List.Pred p){ int c = 0; for(X x:this) if(p.huh(x))c++; return c; } static class RMDup extends List.Fold>{ Comp c; RMDup(Comp cc){ this.c = cc; } public List fold(X x, List l){ if(l.contains(this.c.curry(x)))return l; return l.push(x); } } /** Remove duplicate items from this list */ public List removeDuplicates(){ return removeDuplicates(new EqualComp()); } /** Remove duplicate items from this list */ public List removeDuplicates(Comp c){ return this.fold(new RMDup(c), List.create()); } /** Fold this List to a single Value (Left to Right)*/ public Y fold(List.Fold f, Y b){ return foldl(f,b); } /** Fold this List to a single Value (Left to Right)*/ public Y foldl(List.Fold f, Y b){ for(X x:this)b = f.fold(x, b); return b; } /** Fold this List to a single Value (Right to Left)*/ public Y foldr(List.Fold f, Y b){ return reverse().foldl(f, b); } /** Apply a function to each Element of this List */ public List map(List.Map m){ List ret = List.create(); for(X x:reverse())ret = ret.push(m.map(x)); return ret; } /** Add an Element to this list at the given index */ public List add(X a, int i){ int j = i; List front = List.create(), back = this; while(!back.isEmpty()){ X x = back.top(); back = back.pop(); if(i-- == 0)return back.push(x).push(a).revpush(front); front = front.push(x); } if(i == 0)return front.push(a).reverse(); throw new RuntimeException("Bad Add Index: "+j); } /** Remove an Element from this list at the given index */ public List remove(int i){ int j = i; List front = List.create(), back = this; while(!back.isEmpty()){ X x = back.top(); back = back.pop(); if(i-- == 0)return back.revpush(front); front = front.push(x); } throw new RuntimeException("Bad Remove Index: "+j); } /** Insert a number of Elements into this SORTED list */ public List insert(Iterable xs, List.Comp c){ List ths = this; for(X x:xs)ths.insert(x, c); return ths; } /** Insert an Element into this SORTED list using the given Comparison */ public List insert(X a, List.Comp c){ List front = List.create(), back = this; while(!back.isEmpty()){ X x = back.top(); if(c.comp(a, x))return back.push(a).revpush(front); back = back.pop(); front = front.push(x); } return front.push(a).reverse(); } /** Sort this List using the given Comparison */ public List sort(List.Comp c){ return mergeSort(c); } /** Sort this List using Insertion Sort */ public List insertionSort(List.Comp c){ List srt = List.create(); for(X x:this)srt = srt.insert(x, c); return srt; } /** Convert this List into an Array, starting at Index 'i' */ public X[] toArray(X[] arr){ int i = 0; for(X x:this)arr[i++] = x; return arr; } /** Return an Iterator for this list */ public Iterator iterator(){ return new ListIterator(this); } /** An Iterator class for functional Lists */ static class ListIterator implements Iterator{ List inner; ListIterator(List i){ this.inner = i; } public boolean hasNext(){ return !this.inner.isEmpty(); } public X next(){ if(!hasNext())throw new NoSuchElementException("next()"); X t = this.inner.top(); this.inner = this.inner.pop(); return t; } public void remove(){ throw new UnsupportedOperationException("remove()"); } } /** Zip two lists (this, and 'l') into a single list, one element at a time */ public List zip(List.Zip z, List ys){ List zs = List.create(); List xs = this; while(!xs.isEmpty() && !ys.isEmpty()){ zs = zs.push(z.zip(xs.top(), ys.top())); xs = xs.pop(); ys = ys.pop(); } return zs.reverse(); } /** Build a list from the numbers 0..(len-1) */ public static List buildlist(List.Build b, int len){ List lst = List.create(); while(len-- > 0)lst = lst.push(b.build(len)); return lst; } /** Replace the element at index 'i' with 's' */ public List replace(int i, X s){ int j = i; List front = List.create(), back = this; while(!back.isEmpty()){ X x = back.top(); back = back.pop(); if(i-- == 0)return back.push(s).revpush(front); front = front.push(x); } throw new RuntimeException("Bad Replace Index: "+j); } /** Replace the first occurrence of 't' with 's' */ public List replace(X t, X s){ return replace(new EqualComp().curry(t),s); } /** Replace the first matching X with 's' */ public List replace(List.Pred p, X s){ List front = List.create(), back = this; while(!back.isEmpty()){ X x = back.top(); back = back.pop(); if(p.huh(x))return back.push(s).revpush(front); front = front.push(x); } throw new RuntimeException("Bad Replace: "+p); } /** Replace all occurrences of 't' with 's' */ public List replaceAll(X t, X s){ return replaceAll(new EqualComp().curry(t),s); } /** Replace all matching Xs with 't' */ public List replaceAll(List.Pred p, X x){ List front = List.create(), back = this; while(!back.isEmpty()){ X xx = back.top(); back = back.pop(); front = front.push(p.huh(xx)?x:xx); } return front.reverse(); } /** Equals for Lists... */ public abstract boolean equals(Object o); /** HashCode for Lists... */ public abstract int hashCode(); private static class FB{ List front, back; FB(List f, List b){ this.front = f; this.back = b; } public String toString(){ return this.front+"::"+this.back; } } private FB frontBack(int i){ List front = List.create(), back = this; while(i-- > 0 && !back.isEmpty()){ front = front.push(back.top()); back = back.pop(); } return new FB(front,back); } private static List merge(List a, List b, List.Comp c){ List ret = List.create(); for(X x:a){ while(!b.isEmpty() && c.comp(b.top(), x)){ ret = ret.push(b.top()); b = b.pop(); } ret = ret.push(x); } for(X x:b)ret = ret.push(x); return ret.reverse(); } private List mergeSort(List.Comp c){ if(length() < 2)return this; FB fb = frontBack(length()/2); return merge(fb.front.mergeSort(c),fb.back.mergeSort(c),c); } /** Convert this List into a java.util.List */ public java.util.List toJavaList(){ java.util.List l = new java.util.ArrayList(); for(X x:this)l.add(x); return l; } /** Return a sublist, starting at index i, with length k */ public List sublist(int i, int k){ if(length() < i+k) return List.create(); return pop(i).reverse(k).reverse(); } /** Shuffle the elements of this list randomly */ public List shuffle(){ List lst = List.create(); for(X x:this) lst = lst.add(x, (int)(Math.random()*(lst.length()+1))); return lst; } }