// CS U213 Spring 2007 // Lecture 11: February 1, 2007 /* +----------------------+ | LoB |<-----------------+ +----------------------+ | +----------------------+ | | int count() | | | int totalPrice() | | | LoB allBefore(int x) | | +----------------------+ | | | / \ | --- | | | ----------------------------- | | | | +----------------------+ +-------------------------+ | | MtLoB | | ConsLoB | | +----------------------+ +-------------------------+ | +----------------------+ +-| Book first | | | int count() | | | LoB rest |---+ | int totalPrice() | | +-------------------------+ | LoB allBefore(int x) | | | int count() | +----------------------+ | | int totalPrice() | | | LoB allBefore(int year) | | +-------------------------+ v +---------------+ | Book | +---------------+ | String title | | String author | | int year | | int price | +---------------+ Given the preceding class dictionary, we would like to design methods to answer the following questions (a) Count how many books we have in an LoB (b) Compute the total price of all books in an LoB (c) Given a date, YYYY, and an LoB, loBooks, produce a list of all books in loBooks published before YYYY Let's make some examples ------------------------- //Books Book htdp = new Book("HtDP", "MF", 2001, 60); Book lpp = new Book("LPP", "STX", 1942, 25); Book ll = new Book("LL", "FF", 1986, 10); // lits of Books LoB mtlist = new MtLoB(); LoB lista = new ConsLoB(this.lpp, this.mtlist); LoB listb = new ConsLoB(this.htdp, this.mtlist); LoB listc = new ConsLoB(this.lpp, new ConsLoB(this.ll, this.listb)); LoB listd = new ConsLoB(this.htdp, new ConsLoB(this.ll, new ConsLoB(this.lpp, this.mtlist))); For each of the questions (a) - (c) an LoB will need to provide an answer. This means we need 3 method signatures in the interface LoB In LoB ------- // count the books in this list int count(); // produce a list of all books published before the given date // from this list of books LoB allBefore(int year); // calculate the total price of all books in this list int totalPrice(); Now let's take care of MtLoB and ConsLoB. First, examples! Examples -------- mtlist.count() => 0 mtlist.totalPrice() => 0 mtlist.allBefore() => mtlist In MtLoB: --------- // count the books in this list int count(){return 0;} // produce a list of all books published before the given date // from this empty list of books LoB allBefore(int year) { return this; } // calculate the total price of all books in this list int totalPrice() { return 0; } and ConsLoB. First, examples! Examples -------- lista.count() => 1 listc.count() => 3 lista.totalPrice() => 25 listd.totalPrice() => 95 lista.allBefore(2000) => lista listb.allBefore(2000) => mtlist listc.allBefore(2000) => new ConsLoB(lpp, new ConsLoB(ll,mtlist)) Now we need a template. A template serves as a starting point for *any* method inside ConsLoB. So we will use generic names for the return type of the method and the method name, e.g. ReturnType, methodName. For each specific method we are about to develop, we will change this names accordingly, e.g. ReturnType => int, methodName => count. TEMPLATE: --------- ReturnType methodName(...) { ... this.first ... -- Book ... this.rest ... -- LoB ... this.rest.count() ... -- int ... this.rest.totalPrice() ... -- int ... this.rest.allBefore(int year) ... -- LoB } In ConsLoB ---------- // count the books in this list int count(){ return 1 + this.rest.count(); } // produce a list of all books published before the given date // from this empty list of books LoB allBefore(int year) { if (this.first.producedBefore(year)) return new ConsLoB(this.first, this.rest.allBefore(year)); else return this.rest.allBefore(year); } // calculate the total price of all books in this list int totalPrice() { return this.first.price + this.rest.totalPrice() ; } Accumulators ------------- We can define our methods in accumulator style. As we did in Scheme, we will design a helper method that takes one more argument, the accumulator. The accumulator at any given point in the computation will hold the "answer" to our question thus far. in LoB -------------- // add to acc the count of books in this list int countAcc(int acc); // add to acc the price of the books in this list. int totalPriceAcc(int acc); // cons to acc the books in this list that are before year. LoB allBeforeAcc(int year, LoB acc); Note how the type of aour accumulator matches the method's return type. In the case of the empty list of books, return the accumulator. in MtLoB: --------- // add to acc the count of books in this list int countAcc(int acc) { return acc; } // add to acc the price of the books in this list. int totalPriceAcc(int acc) { return acc; } // cons to acc the books in this list that are before year. LoB allBeforeAcc(int year, LoB acc){ return acc; } Let's review out template for ConsLoB. TEMPLATE: --------- ReturnType methodName(...) { ... this.first ... -- Book ... this.rest ... -- LoB ... this.rest.count() ... -- int ... this.rest.countAcc(int acc) ... -- int ... this.rest.totalPrice() ... -- int ... this.rest.totalPriceAcc(int acc) ... -- int ... this.rest.allBefore(int year) ... -- LoB ... this.rest.allBeforeAcc(int year, LoB acc) ... -- LoB } in ConsLoB: ----------- // add to acc the count of books in this list int countAcc(int acc) { return this.rest.countAcc(acc + 1); } // add to acc the price of the books in this list. int totalPriceAcc(int acc) { return this.rest.totalPriceAcc(this.first.price + acc); } // cons to acc the books in this list that are before year. LoB allBeforeAcc(int year, LoB acc){ if (this.first.producedBefore(year)) return this.rest.allBeforeAcc(year, new ConsLoB(this.first, acc)); else return this.rest.allBeforeAcc(year, acc); } Using the same tests as before, our accumulator methods should give us the same results. *I run my tests and I got an error!* Why? Here is one of the tests that failed: (check this.listc.allBeforeAcc(2001, this.mtlist) expect new ConsLoB(this.lpp, new ConsLoB(this.ll, this.mtlist))) my example says listc.allBefore(2000) => new ConsLoB(lpp, new ConsLoB(ll,mtlist)) since the accumulator version of allBefore should give the same result I expected listc.allBeforeAcc(2000,mtlist) => new ConsLoB(lpp, new ConsLoB(ll,mtlist)) But this test fails. Let's trace our code. We will write each call on a new line. (new ConsLob(lpp, new ConsLoB(ll,mtlist))).allBeforeAcc(2000,mtlist) [method-call] => (new ConsLob(lpp, new ConsLoB(ll,mtlist))).first.producedBefore(2000) [if-test] => lpp.producedBefore(2000) [this.first] => return lpp.year < 2000 [method-call] => return 1942 < 2000 [lpp.year] => true [ < ] => return (new ConsLob(lpp, new ConsLoB(ll, mtlist))).rest.allBeforeAcc(2000, new ConsLoB(lpp, mtlist)); [if-branch-true] => return (new ConsLoB(ll, mtlist)).allBeforeAcc(2000, new ConsLoB(lpp, mtlist)); [this.rest] => (new ConsLoB(ll, mtlist)).first.producedBefore(2000); [if-test] => ll.producedBefore(2000); [this.first] => return ll.year < 2000 [method-call] => return ll.1986 < 2000 [ll.year] => return true [ < ] => (new ConsLoB(ll, mtlist)).rest.allBeforeAcc(2000, new ConsLoB(ll, new ConsLoB(lpp, mtlist))) [if-branch-true] => mtlist.allBeforeAcc(2000, new ConsLoB(ll, new ConsLoB(lpp, mtlist))) [this.rest] => return new ConsLoB(ll, new ConsLoB(lpp, mtlist) [method-call] => new ConsLoB(ll, new ConsLoB(lpp, mtlist) [return] OUPS! The result is not what I expected. The elements in the list are reversed! This is why my test fails. The answer given by the program is correct, but the equality test fails. Since the result is correct, I can change my expected result in my test. */ // to represent a book class Book { String title; String author; int year; int price; Book(String title, String author, int year, int price) { this.title = title; this.author = author; this.year = year; this.price = price; } // was this book produced before the given year? boolean producedBefore(int year){ return this.year < year; } } // to represent a list of books interface LoB { // count the books in this list int count(); // add to the count the count of all books in this list int countAcc(int acc); // calculate the total price of all books in this list int totalPrice(); // add to the accumulator the total price of all books in this list int totalPriceAcc(int acc); // produce a list of all books published before the given date // from this list of books LoB allBefore(int year); // add to the given accumulator a list of all books that were // published before the given date from this list of books LoB allBeforeAcc(int year, LoB acc); } // to represent an empty list of books class MtLoB implements LoB { MtLoB() { } // count the books in this list int count(){return 0;} // add to the count the count of all books in this list int countAcc(int acc){ return acc; } // calculate the total price of all books in this list int totalPrice() { return 0; } // add to the accumulator the total price of all books in this list int totalPriceAcc(int acc){ return acc; } // produce a list of all books published before the given date // from this empty list of books LoB allBefore(int year) { return this; } // add to the given accumulator a list of all books that were // published before the given date from this empty list of books LoB allBeforeAcc(int year, LoB acc){ return acc; } } // to represent a nonempty list of books class ConsLoB implements LoB { Book first; LoB rest; ConsLoB(Book first, LoB rest) { this.first = first; this.rest = rest; } /* TEMPLATE: ReturnType methodName(...) { ... this.first ... -- Book ... this.rest ... -- LoB ... this.rest.count() ... -- int ... this.rest.countAcc(int acc) ... -- int ... this.rest.countAccUp(int acc) ... -- int ... this.updateCount(Book b, int acc) ... -- int ... this.rest.allBefore(int year) ... -- LoB ... this.rest.allBeforeAcc(int year, LoB acc) ... -- LoB ... this.rest.allBeforeAccUp(int year, LoB acc) ... -- LoB ... this.updateAllBefore(int year, Book b, Lob acc) -- LoB } */ // count the books in this list int count(){ return 1 + this.rest.count();} // add to the count the count of all books in this list int countAcc(int acc){ return this.rest.countAcc(1 + acc); } // calculate the total price of all books in this list int totalPrice() { return this.first.price + this.rest.totalPrice(); } // add to the accumulator the total price of all books in this list int totalPriceAcc(int acc){ return this.rest.totalPriceAcc(this.first.price + acc); } // produce a list of all books published before the given date // from this empty list of books LoB allBefore(int year) { if (this.first.producedBefore(year)) return new ConsLoB(this.first, this.rest.allBefore(year)); else return this.rest.allBefore(year); } // add to the given accumulator a list of all books that were // published before the given date from this empty list of books LoB allBeforeAcc(int year, LoB acc){ if (this.first.producedBefore(year)) return this.rest.allBeforeAcc(year, new ConsLoB(this.first, acc)); else return this.rest.allBeforeAcc(year, acc); } // update the accumulated list by adding the given book if appropriate LoB updateAllBefore(int year, Book b, LoB acc){ if (b.producedBefore(year)) return new ConsLoB(b, acc); else return acc; } } class Examples{ Examples(){} Book htdp = new Book("HtDP", "MF", 2001, 60); Book lpp = new Book("LPP", "STX", 1942, 10); Book ll = new Book("LL", "FF", 1986, 25); // tests for the method producedBefore: boolean testProducedBefore(){ return (check this.htdp.producedBefore(1950) expect false) && (check this.lpp.producedBefore(1950) expect true); } LoB mtlist = new MtLoB(); LoB lista = new ConsLoB(this.lpp, this.mtlist); LoB listb = new ConsLoB(this.htdp, this.mtlist); LoB listc = new ConsLoB(this.lpp, new ConsLoB(this.ll, this.listb)); LoB listd = new ConsLoB(this.htdp, new ConsLoB(this.ll, new ConsLoB(this.lpp, this.mtlist))); // tests for the method allBefore boolean testBefore(){ return (check this.mtlist.allBefore(2001) expect this.mtlist) && (check this.lista.allBefore(2001) expect this.lista) && (check this.listb.allBefore(2001) expect this.mtlist) && (check this.listc.allBefore(2001) expect new ConsLoB(this.lpp, new ConsLoB(this.ll, this.mtlist))) && (check this.listd.allBefore(2001) expect new ConsLoB(this.ll, new ConsLoB(this.lpp, this.mtlist))); } boolean testTotalPrice() { return (check this.mtlist.totalPrice() expect 0) && (check this.lista.totalPrice() expect 10) && (check this.listc.totalPrice() expect 95) && (check this.listd.totalPrice() expect 95); } boolean testTotalPriceAcc() { return (check this.mtlist.totalPriceAcc(0) expect 0) && (check this.lista.totalPriceAcc(0) expect 10) && (check this.listc.totalPriceAcc(0) expect 95) && (check this.listd.totalPriceAcc(0) expect 95); } // tests for the method allBefore boolean testBeforeAcc(){ return (check this.mtlist.allBeforeAcc(2001, this.mtlist) expect this.mtlist) && (check this.lista.allBeforeAcc(2001, this.mtlist) expect this.lista) && (check this.listb.allBeforeAcc(2001, this.mtlist) expect this.mtlist) && // The following 2 tests FAIL! Read the last section of the comment block // in the beggining of the file. Pay attention to the trace provided there. //(check this.listc.allBeforeAcc(2001, this.mtlist) // expect new ConsLoB(this.lpp, // new ConsLoB(this.ll, this.mtlist))) && //(check this.listd.allBeforeAcc(2001, this.mtlist) // expect new ConsLoB(this.ll, // new ConsLoB(this.lpp, this.mtlist))) // These 2 tests succeed. The result list from the accumulator function // has the books in reverse order. (check this.listc.allBeforeAcc(2001, this.mtlist) expect new ConsLoB(this.ll, new ConsLoB(this.lpp, this.mtlist))) && (check this.listd.allBeforeAcc(2001, this.mtlist) expect new ConsLoB(this.lpp, new ConsLoB(this.ll, this.mtlist))); } }