/* --- CSU213 Spring 2006 Lecture Notes --------- Copyright 2006 Viera K. Proulx Lecture 13a: A List of Valid Names Goals: - Practice designing methods for lists Introduction: One of the problems in the homework assignment concerned the question whether all names in a given list are valid names. Of course, valid names are themselves given as a list of names. The problem illustrates the use of the design recipe in unraveling this problem's complexity. ------------------------------------------------------------------------ We start with the problem analysis. The information available is represented as two lists of names (String-s). The classes that represent these strings are ILoString, MTLoString, and ConsLoString, the first of them a common interface, the second representing an empty list of Strings, and the third a class with fields 'first' of the type String and 'rest' of the type ILoString. We know nothing specific about these lists. To be able to discuss them, we label them as valid-list and name-list. We can now try to formulate the purpose statement --- the question our program should answer: 'Is every element of name-list an element of valid-list.' It is clear that we need to design a method for the list of names. The question is, whether 'this' should be the name-list of the valid-list. Looking at the problem statement we see that we are answering a question about every element of name-list. Therefore, name-list should invoke the method: we are asking questions about name-list, while the valid-list is just a piece of information we need to be able to answer the question. In ProfessoJ we use the names nameList and validList and design the following purpose and header: // is every element of this list an element of the given validList? boolean validNames(ILoString validList); We use Scheme notation as a shorthand for describing the data we use in our examples: validList:(list "red" "green" "black" "blue" "white") nameList0: empty nameList1: (list "red" "black" "green") nameList2: (list "red" "blue" "yellow" "green") nameList0.validNames(validList) --> true nameList1.validNames(validList) --> true nameList2.validNames(validList) --> false For the empty case the answer is always true and we are done. For the ConsLostring we continue with the template: // TEMPLATE: ... this.first ... -- String ... this.rest ... -- ILoString ... this.rest.validNames(validList) ... -- boolean and we read aloud (or write down) the purpose statement for the last entry in the template: Is every element of the rest of this list an element of the given validList? Well, with that taken care of, we only need to find out whether the first element of this list is an element of the validList. But that itself is a hard question to answer. Again, it is time to bring in a helper method, but now, the tables are reversed. The name is represented as a String and the question whether it is contained in some list should be answered by a method in the classes that represent the list. So, we have the following purpose statement and header: // does this list contain the given name? boolean contains(String name) We leave it to the reader to design this method. Our template now has an additional entry: // TEMPLATE: ... this.first ... -- String ... this.rest ... -- ILoString ... this.rest.validNames(validList) ... -- boolean ... validList.contains(..String..) ... -- boolean Note that the following are also correct entries in the template, but are meaningless in our context: ... validList.validNames(.. ILoString..) ... -- boolean ... this.rest.contains(..String..) ... -- boolean As an exercise, read the purpose statements for each of these template entries and explain why it does not help us. If we substitute 'this.first' for the argument in the last entry in our template, our problem is solved: // is every element of this list an element of the given validList? boolean validNames(ILoString validList){ return validList.contains(this.first) && this.rest.validNames(validList); } Of coourse, we now run the tests. Complete code follows: /* +-----------+ | ILoString |<---------------+ +-----------+ | +-----------+ | / \ | --- | | | --------------------- | | | | +------------+ +----------------+ | | MTLoString | | ConsLoString | | +------------+ +----------------+ | +------------+ | String first | | | ILoString rest |----+ +----------------+ */ // to represent a list of Strings interface ILoString { // is every element of this list an element of the given validList? boolean validNames(ILoString validList); // does this list contain the given name? boolean contains(String name); } // to represent an empty list of Strings class MTLoString implements ILoString { MTLoString() { } // is every element of this list an element of the given validList? boolean validNames(ILoString validList){ return true; } // does this list contain the given name? boolean contains(String name){ return false; } } // to represent a nonempty list of Strings class ConsLoString implements ILoString { String first; ILoString rest; ConsLoString(String first, ILoString rest) { this.first = first; this.rest = rest; } /* TEMPLATE: ... this.first ... -- String ... this.rest ... -- ILoString ... this.rest.validNames(validList) ... -- boolean ... validList.contains(..String..) ... -- boolean */ // is every element of this list an element of the given validList? boolean validNames(ILoString validList){ return validList.contains(this.first) && this.rest.validNames(validList); } // does this list contain the given name? boolean contains(String name){ return this.first.equals(name) || this.rest.contains(name); } } class Examples { Examples() {} ILoString empty = new MTLoString(); ILoString validList = new ConsLoString("red", new ConsLoString("green", new ConsLoString("black", new ConsLoString("blue", new ConsLoString("white", this.empty))))); ILoString nameList0 = new MTLoString(); ILoString nameList1 = new ConsLoString("red", new ConsLoString("black", new ConsLoString("green", this.empty))); ILoString nameList2 = new ConsLoString("red", new ConsLoString("blue", new ConsLoString("yellow", new ConsLoString("green", this.empty)))); // tests for the method validNames boolean testValidNames0 = this.nameList0.validNames(this.validList) == true; boolean testValidNames1 = this.nameList1.validNames(this.validList) == true; boolean testValidNames2 = this.nameList2.validNames(this.validList) == false; // tests for the method contains boolean testContains0 = this.nameList0.contains("red") == false; boolean testContains1 = this.nameList1.contains("black") == true; boolean testContains2 = this.nameList1.contains("yellow") == false; }