/* Csu213, Thursday 3/15/07 Again I suggest reading these notes in a good syntax-highlighted editor. We continue the interfaces (traversals etc.) from last time and keep our (new) favorite generic List classes... Once we get the definitions out of the way, we can start on some new stuff. First we define our usual interfaces, Lists, and Traversals. */ // For Later On... import java.util.ArrayList; import java.util.Random; // Our usual Selector interface interface ISelect{ boolean select(T t); } // ISame, incase we need it interface ISame{ boolean same(T t); } // Represents a generic 'linear' traversal interface Traversal{ boolean isEmpty(); T getFirst(); Traversal getRest(); } // Generic List Union interface AList extends Traversal{ // ** Note that isEmpty(), getFirst() and getRest() are also added // (abstractly) to this class by the 'implementation' of Traversal } // Represents an Empty List of T class MtList implements AList{ // Basic Constructor public MtList(){} // Traversal functions so that things like 'filter' can be // written without disturbing the list classes public boolean isEmpty(){ return true; } public T getFirst(){ throw new NoFirstElementException(); } public Traversal getRest(){ throw new NoRestElementException(); } public String toString(){ return "Mt()"; } } // Represents a Non-empty List of T class ConsList implements AList{ T first; AList rest; // Basic Constructor public ConsList(T first, AList rest){ this.first = first; this.rest = rest; } // Traversal functions so that things like 'filter' can also be // written without disturbing the list classes public boolean isEmpty(){ return false; } public T getFirst(){ return first; } public Traversal getRest(){ return rest; } public String toString(){ return "Cons( "+first+", "+rest+")"; } } // Throw when No 'First' Element class NoFirstElementException extends RuntimeException{ public NoFirstElementException(){ super("No First Element"); } } // Throw when No 'Rest' Element class NoRestElementException extends RuntimeException{ public NoRestElementException(){ super("No Rest Element"); } } // First attempt at a generic filter algorithm class Alg1{ // Filter the Traversal based on the given Selector static AList filter(Traversal tr, ISelect pick){ if(tr.isEmpty()) return new MtList(); else if(pick.select(tr.getFirst())) return new ConsList(tr.getFirst(), filter(tr.getRest(), pick)); else return filter(tr.getRest(), pick); } } /* One thing to notice about the filter function above is that it can only produce a List. We can change/fix this by creating a new interface for DataSets which can be added to. */ // Interface for a collection of data which can be added to and later traversed interface DataSet{ DataSet add(T t); Traversal getTraversal(); } /* The reason we need the getTraversal() method is because once we have a DataSet, we don't want to just keep adding things to it, so we can get a Traversal and do some interesting stuff. Now we have to create some DataSets, and a few functions which can use them. What we can do is Wrap existing data structures so the interface can be implemented without changing their earlier code */ // Wraps an immutable List as a DataSet class ListWrapper implements DataSet{ AList list; // Clients can only create an empty ListWrapper public ListWrapper(){ this(new MtList()); } // Private constructor gets passed a List private ListWrapper(AList list){ this.list = list; } // DataSet Function... add an element to the internal list public ListWrapper add(T t){ return new ListWrapper(new ConsList(t, list)); } // Return the List as a Traversal public Traversal getTraversal(){ return list; } } /* Now for the (seemingly) impossible dream; BSTs... We've been talking about how all this stuff abstracts over the data structures used, so before we jump to ArrayLists and lots of mutation, I'll show you what we mean by general Traversal/DataSet with BSTs. But, BSTs need a notion of less-than to order elements so we need a little more infrastructure first. */ // Interface for less than comparison... kinda like ISame interface LessThan{ boolean lessThan(T t); } // The straight forward BST interface interface BST>{ // Insert the given T into this BST BST insert(T t); // Return the Smallest T smallest(); // Chop off the smallest BST withoutSmallest(); // Is this BST a Leaf boolean isLeaf(); } // Represents a non-empty BST class Node> implements BST{ T data; BST left, right; // Simple Constructor public Node(T d, BST lft, BST rght){ data = d; left = lft; right = rght; } // The usual Insert method... Note: we can have repeated data, it // will just go to the right public BST insert(T t){ if(t.lessThan(data)) return new Node(data, left.insert(t), right); else return new Node(data, left, right.insert(t)); } // Return the smallest element == farthest Left public T smallest(){ if(left.isLeaf()) return data; else return left.smallest(); } // Remove the smallest element public BST withoutSmallest(){ if(left.isLeaf())return right; else return new Node(data, left.withoutSmallest(), right); } // Definately not a Leaf! public boolean isLeaf(){ return false; } } // Represents the empty BST class Leaf> implements BST{ // The Default Constructor is just fine // Insert a T into this Leaf public BST insert(T t){ return new Node(t, this, this); } // No smallest, so we say Error public T smallest(){ throw new NoFirstElementException(); } // No without smallest, so we say Error public BST withoutSmallest(){ throw new NoRestElementException(); } // It's a Leaf! public boolean isLeaf(){ return true; } } /* Ok, there's our blessed Binary Search Tree we've been talking so much about. So now how can we use it in some more general algorithms? Well, as you may have guessed... we wrap it! */ // Wraps a BST so we can use it as a DataSet and/or a Traversal class BSTWrapper> implements DataSet, Traversal{ BST bst; // Public Default Constructor, starts empty public BSTWrapper(){ this(new Leaf()); } // Public, Wraps a given BST public BSTWrapper(BST b){ bst = b; } // Add a given T to this BST, and Wrap the result public BSTWrapper add(T t){ return new BSTWrapper(bst.insert(t)); } // Return 'this' as a Traversal public Traversal getTraversal(){ return this; } // Translation of the BST functions to our Traversal interface public boolean isEmpty(){ return bst.isLeaf(); } public T getFirst(){ return bst.smallest(); } // Here we need to wrap the result again public Traversal getRest(){ return new BSTWrapper(bst.withoutSmallest()); } } /* Now we can do stuff like implementing filter without using any constructors */ // Use our new DataSets to build a completely general filter function class Alg2{ // Filter the Traversal into the DataSet static DataSet filterAcc(Traversal tr, ISelect pick, DataSet acc){ if(tr.isEmpty()) return acc; else return filterAcc(tr.getRest(), pick, updateAcc(pick, tr.getFirst(), acc)); } // Update the accumulator if the given T is to be selected static DataSet updateAcc(ISelect pick, T that, DataSet acc){ if(pick.select(that)) return acc.add(that); else return acc; } } /* So on to ArrayLists. Using our new-found interfaces, we can wrap the AL but we have to be careful since it relies on side-effects. The DataSet in turn has to use mutation, since the AL does, but we can do our Traversal without it, assuming no one is removing from our list while we are traversing. */ // Wraps an ArrayList as a DataSet class ALWrapper implements DataSet{ ArrayList list; // Clients can only create an empty ListWrapper public ALWrapper(){ list = new ArrayList(); } // DataSet Function... add an element to the internal list public ALWrapper add(T t){ list.add(t); return this; } // Allow access to the internal List public ArrayList getList(){ return list; } // Return the List as a Wrapped Traversal public Traversal getTraversal(){ return new ALTraversal(list); } } // Wraps an ArrayList as a Traversal class ALTraversal implements Traversal{ ArrayList list; int current; // Cients can only start at 0 public ALTraversal(ArrayList lst){ this(lst, 0); } // Private, Creates a Traversal starting at a given index private ALTraversal(ArrayList lst, int curr){ list = lst; current = curr; } // Are we at the end of the List? public boolean isEmpty(){ return current >= list.size(); } // Get the current element; it's an error if we're empty public T getFirst(){ if(isEmpty()) throw new NoFirstElementException(); return list.get(current); } // Create a new traversal for the next element public Traversal getRest(){ if(isEmpty()) throw new NoRestElementException(); return new ALTraversal(list, current+1); } } /* Because we have all these great general interfaces and implementations guess what we can do? We can write General Tests!! Really we can write general everything, but I'll show you some interesting things we can do with these interfaces. First we define a simple IntWrapper which implements LessThan then a few simple but interesting ISelects to test. */ // Wraps an integer so we can compare it using LessThan class IntWrap implements LessThan, ISame{ int i; IntWrap(int i){ this.i = i; } // LessThan Interface public boolean lessThan(IntWrap that){ return this.i < that.i; } // ISame Interface public boolean same(IntWrap that){ return this.i == that.i; } // Common toString() public String toString(){ return ""+i; } } class Greater implements ISelect{ int cutoff; Greater(int c){ cutoff = c; } public boolean select(IntWrap i){ return i.i > cutoff; } } class Even implements ISelect{ public boolean select(IntWrap i){ return ((i.i % 2) == 0); } } /* Now here comes the meat of what we can do... these are very general functions which produce DataSets and do some reasonably cool stuff without ever knowing exactly what type of data they are operating on. Notice how easy it is to sort once we have a BST DataSet. Also see how we can create some random test data, print it out, and check it for equality. */ // a few useful functions class Functions{ // Accumulate some random numerical data into a DataSet static DataSet fillData(DataSet acc, int howMany){ if(howMany <= 0) return acc; else return fillData(acc.add(randomInt()), howMany-1); } // Get a Random IntWrap static IntWrap randomInt(){ return new IntWrap(new Random().nextInt(30)); } // Sort a Given Traversal using a BST DataSet static > DataSet sort(Traversal tr){ return sortAcc(tr, new BSTWrapper()); } // Use the BST accumulator to collect the stuff the result is then // a DataSet which can be traversed in the correct order (smallest // to largest) static > DataSet sortAcc(Traversal tr, BSTWrapper acc){ if(tr.isEmpty()) return acc; else return sortAcc(tr.getRest(), acc.add(tr.getFirst())); } // Print out a list looking representation of a Traversal static String toString(Traversal tr){ return "[ "+simpleString(tr)+"]"; } private static String simpleString(Traversal tr){ if(tr.isEmpty()) return ""; else return tr.getFirst()+" "+simpleString(tr.getRest()); } // See if two traversals contain the same oredered elements static > boolean same(Traversal tA, Traversal tB){ if(tA.isEmpty() && tB.isEmpty()) return true; else if(tA.isEmpty() || tB.isEmpty()) return false; else return (tA.getFirst().same(tB.getFirst()) && same(tA.getRest(), tB.getRest())); } } /* Finally we show how all this falls togther and how it's all called/constructed. I also wrap some of the functions to save typing... that's a recuring theme. */ public class notes_3_15_07{ // We wrap println... static void print(String s){ System.out.println(s); } // We wrap the Functions.toString so that there's much less typing below static String string(DataSet dat){ return Functions.toString(dat.getTraversal()); } // Same for filter... less typing static DataSet filter(Traversal tr, ISelect pick){ return Alg2.filterAcc(tr, pick, new ListWrapper()); } // Same for 'same' and 'sort' static boolean same(DataSet dA, DataSet dB){ return Functions.same(dA.getTraversal(), dB.getTraversal()); } static DataSet sort(DataSet dat){ return Functions.sort(dat.getTraversal()); } public static void main(String args[]){ DataSet list1 = Functions.fillData(new ListWrapper(), 10); DataSet list2 = Functions.fillData(new ListWrapper(), 10); DataSet alist1 = Functions.fillData(new ALWrapper(), 10); DataSet alist2 = Functions.fillData(new ALWrapper(), 10); print(" List Tr : "+string(list1)); print(" nums > 15 : "+string(filter(list1.getTraversal(), new Greater(15)))); print(" Sorted : "+string(sort(list1))); print(""); print(" List Tr : "+string(list2)); print(" Evens : "+string(filter(list2.getTraversal(), new Even()))); print(" Sorted : "+string(sort(list2))); print(""); print(" ArrList Tr : "+string(alist1)); print(" nums > 15 : "+string(filter(alist1.getTraversal(), new Greater(15)))); print(" Sorted : "+string(sort(alist1))); print(""); print(" ArrList Tr : "+string(alist2)); print(" Evens : "+string(filter(alist2.getTraversal(), new Even()))); print(" Sorted : "+string(sort(alist2))); DataSet small = Functions.fillData(new ListWrapper(), 5); print(""); print(" small : "+string(small)); print(" sorted(small) : "+string(sort(small))); print(" same(small, small) : "+same(small, small)); print(" same(small, sorted(small)) : "+same(small, sort(small))); } }