/* --- CSU213 Spring 2005 Lecture Notes --------- Copyright 2005 Viera K. Proulx Lecture 19: Which Way to the Magic Mountain? ... */ /* Goals: - to design a program where accumulators are necessary - to learn to traverse a circular structure Introduction: Again, we seek to find out whether there is a path from one station in our transit tystem to another station. However, we change the constraints on our system and the way we represent it. The system is represented as a list of segments, where each segment is just a pair fo String-s that represent the origin and destination of that route segment. The origin and destination are stations in the transit system. Each station is the origin of exactly one segment. The last restriction means that we can go somewhere from each station, and that we can only go in one direction. It is a draconian constraint, but it is sufficient to illustrate a serious problem we encounter when searching for a routing from one station to another. Here is an example of a transit system: A -> B B -> C D -> E E -> B C -> E The data that represent this transit is ALoRt transit = new ConsLoRt(new Segment("A", "B"), new ConsLoRt(new Segment("B", "C"), new ConsLoRt(new Segment("D", "E"), new ConsLoRt(new Segment("E", "B"), new ConsLoRt(new Segment("C", "E"), new MTLoRt()))))); We ask whether there is a routing from D to C. The method (in the classes that represent the transit) consumes the 'from' and 'to' String-s and produces a boolean value: // is there a path in this transit between the given stations boolean hasRouting(String orig, String dest) Let us make some examples: boolean testHasRouting1 = transit.hasRouting("D", "C") == true; boolean testHasRouting2 = transit.hasRouting("E", "C") == true; boolean testHasRouting3 = transit.hasRouting("C", "D") == false; boolean testHasRouting4 = transit.hasRouting("C", "C") == true; boolean testHasRouting5 = transit.hasRouting("P", "C") == false; boolean testHasRouting6 = (new MTLoS()).hasRouting("C", "C") == false; boolean testHasRouting7 = (new MTLoS()).hasRouting("E", "C") == false; The first and second examples show that to find a routing from "D" to "C", we first find the segment in which "D" is the origin, check whether the destination is our desired dest. If it is, we found the routing, otherwise we go on with the search, now starting from the destination station in the current segment. Of course, this is the 'general' case. If we are asked (as the fourth example shows) whether there is a path from a given point to itself, the answer is true, as long as this is a station in our transit system. We see that in the classes ALoRt and MTLoRt our work is simple - there is no routing between any two stations in an empty list of segments, and the method produces 'false'. Some of the examples refer to stations that are not in our transit system. We assume that a preliminary screening has been done and we seek routing only between stations that do appear in our transit system. Further analysis of the examples one and two indicates that the transit system used to invoke our method does not change as we proceed with our search - only the arguments our method consumes keep changing. We will use the method findNext that produces the station in the transit system that has the given origin. Here is the header and the purpose statement - we leave it up to you to design the method // find the next station in this transit system after the given station String findNext(String origin){...} In the case of MTLoS, we return an empty String "". Based on the constraints of our system the search should succeed before we exhaust all the segments in our transit, and so this code should not be used. (Later we will learn how to notify the user that an unexpected thing happened.) Here is the template for the method hasRouting: ... this.first ... ... this.first.origin ... ... this.first.destination ... ... this.rest ... ... this.rest.hasRouting(String some-orig, String some-dest) ... ... orig ... ... dest ... Let us think about the body: boolean hasRouting(String orig, String dest){ if (orig.compareTo(seen) == 0) return true; // this follows from the third example else // see if there is a route from the next station after orig // that leads to the given dest - in this same transit !!! return this.hasRouting(this.findNext(origin), dest); } The comments explain our reasoning. We can now run our tests. ..................... - we are still waiting ...................... What is the problem? We stopped the execution and see that there is a problem with the second test case. Let us trace what is happening. Maybe we should have a picture of our transit system: ------------ / \ / v A ----> B ----> C D ----> E ^ / \ / -------------------- We are looking to get from C to D. The sequence of method invocations looks like this: transit.hasRouting("C", "D") --- find next after C and continue: transit.hasRouting("E", "D") --- find next after E and continue: transit.hasRouting("B", "D") --- find next after B and continue: transit.hasRouting("C", "D") --- find next after C and continue: ... but we are back where we started from - and will keep running in circles forever. We lost the knowledge of where we have been and that information is crucial to solving the problem. Following the design recipe for methods (or functions) with accumulators we decide on the purpose for the accumulator: --------------------------- "to record all stations we have already traveled through" The initial value for the accumulator is an empty list. --------------------------------- We use the accumulator to see whether we are again at the station ------------------- we have already traveled through and thus failed to find the routing. We update the accumulator by adding the origin to the list of ---------------------- stations we have seen as we move to the next station. Because we know that the transit system we deal with remains unchanged as we search for the routing, we can leave the methods in the abstract class and in the class MTLoS unchanged. In the class ConsLoS we add a method: // is there a path in this transit between the given stations // that does not go through the seen stations boolean hasNoLoopRouting(String orig, String dest, ALoS seen){ if (orig.compareTo(dest) == 0) return true; // this follows from the third example else if (seen.contains(orig)) return false; else // see if there is a route from the next station after orig // that leads to the given dest - in this same transit !!! return this.hasNoLoopRouting(this.findNext(orig), dest, new ConsLoS(orig, seen)); } We added a clause in the middle that checks whether we are repeating our search (using the accumulator) and construct a new list of stations with the origin added each time we recur (updating the accumulator). We conclude with the new vesrsion of our method hasRouting in the class ConsLoS: boolean hasRouting(String orig, String dest){ return this.hasNoLoopRouting(orig, dest, new MTLoS()); } This is where the accumulator is initialized and the method that uses the accumulator is invoked for the first time. We can now run our program, including the original examples. ------------------------------------------------------------------------ */ /* ; ; ; ; ;;;; ; ; ; ; ; ; ; ; ; ; ;;; ;; ; ;;; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;;;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ;;;; ;;; ;; ; ;;;; ; ; ; */ // to represent a transit route segment class Segment { String origin; String destination; Segment(String origin, String destination) { this.origin = origin; this.destination = destination; } /* // ** DRAFT TEMPLATE ** Edit as needed. ??? mmm() { ... this.origin ... ... this.destination ... } */ } /* ; ; ; ; ;;;;;;; ; ; ; ; ; ; ; ; ; ; ;;; ; ;; ;;; ; ;;;; ; ; ;; ; ; ;; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ;;;; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;;;; ; ; ;;; ; ;; ; ; ; */ // to represent a list of transit route segments abstract class ALoRt { // find the next station in this transit system after the given station abstract String findNext(String origin); // is there a path in this transit between the given stations abstract boolean hasRouting(String orig, String dest); /* // ** DRAFT TEMPLATE ** Edit as needed. // purpose statement abstract ??? mmm(); */ } // to represent an empty list of transit route segments class MTLoRt extends ALoRt { MTLoRt() { } // find the next station in this transit system after the given station String findNext(String origin){ return ""; } boolean hasRouting(String orig, String dest){ return false; } /* // ** DRAFT TEMPLATE ** Edit as needed. ??? mmm() { } */ } // to represent a nonempty list of transit route segments class ConsLoRt extends ALoRt { Segment first; ALoRt rest; ConsLoRt(Segment first, ALoRt rest) { this.first = first; this.rest = rest; } // find the next station in this transit system after the given station String findNext(String origin){ if (this.first.origin.compareTo(origin) == 0) return this.first.destination; else return this.rest.findNext(origin); } boolean hasRouting(String orig, String dest){ return this.hasNoLoopRouting(orig, dest, new MTLoS()); } // is there a path in this transit between the given stations // that does not go through the seen stations boolean hasNoLoopRouting(String orig, String dest, ALoS seen){ if (orig.compareTo(dest) == 0) return true; // this follows from the third example else if (seen.contains(orig)) return false; else // see if there is a route from the next station after orig // that leads to the given dest - in this same transit !!! return this.hasNoLoopRouting(this.findNext(orig), dest, new ConsLoS(orig, seen)); } /* // ** DRAFT TEMPLATE ** Edit as needed. ??? mmm() { ... this.first ... ... this.first.origin ... ... this.first.destination ... ... this.rest ... ... this.rest.hasRouting(String some-orig, String some-dest) ... ... this.rest.hasNoLoopRouting(String some-orig, String some-dest, ALoS seen) ... } */ } /* ; ; ; ; ;;;; ; ; ; ; ; ; ; ; ; ; ;;;; ;;; ;;;; ; ;;; ; ;; ;;; ; ;; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ;;;; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;; ;; ;;;;; ;; ; ;;; ; ; ;;; ; ; ; */ // to represent a list of stations abstract class ALoS { // does this list contain the given station abstract boolean contains(String s); /* // ** DRAFT TEMPLATE ** Edit as needed. // purpose statement abstract ??? mmm(); */ } // to represent an empty list of stations class MTLoS extends ALoS { MTLoS() { } // does this list contain the given station boolean contains(String s){ return false; } /* // ** DRAFT TEMPLATE ** Edit as needed. ??? mmm() { } */ } // to represent an nonempty list of stations class ConsLoS extends ALoS { String first; ALoS rest; ConsLoS(String first, ALoS rest) { this.first = first; this.rest = rest; } // does this list contain the given station boolean contains(String s){ return this.first.compareTo(s) == 0 || this.rest.contains(s); } /* // ** DRAFT TEMPLATE ** Edit as needed. ??? mmm() { ... this.first ... ... this.rest ... } */ } /* ; ; ; ; ;;;;;; ; ; ; ; ; ; ; ; ; ; ; ;;; ; ;; ;; ; ;; ; ;;; ;;; ; ;;;;; ; ; ; ; ;; ;; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ;;;; ; ; ; ; ; ; ;;;;;; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ;;;;;;; ; ;;;;; ; ; ; ; ;; ; ;;;; ;;; ; ; ; ; ; ; */ class Examples{ Examples(){} ALoRt mtransit = new MTLoRt(); ALoRt transit = new ConsLoRt(new Segment("A", "B"), new ConsLoRt(new Segment("B", "C"), new ConsLoRt(new Segment("D", "E"), new ConsLoRt(new Segment("E", "B"), new ConsLoRt(new Segment("C", "E"), new MTLoRt()))))); boolean testHasRouting1 = transit.hasRouting("D", "C") == true; boolean testHasRouting2 = transit.hasRouting("C", "D") == false; boolean testFindNext1 = transit.findNext("D").compareTo("E") == 0; boolean testFindNext2 = transit.findNext("A").compareTo("B") == 0; boolean testContains1 = new ConsLoS("A", new ConsLoS("B", new ConsLoS("C", new MTLoS()))).contains("B") == true; boolean testContains2 = new ConsLoS("A", new ConsLoS("B", new ConsLoS("C", new MTLoS()))).contains("F") == false; }