public Picture smileys1(int levels, Picture p1, Picture p2) { if (levels<1) { return empty(); } else { Picture p3 = smileys1(levels-1,p2,p1); Picture p4 = smileys1(levels-1,p1,p2); return fourPics(p1,p3,clockwise270(p4),clockwise90(p2)); } }
public Picture smileys2(int levels, Picture p1, Picture p2, Picture p3, Picture p4) { if (levels<=1) { return p1; } else { Picture p5 = smileys2(levels-1,p2,p4,p3,p1); Picture p6 = smileys2(levels-2,p4,p3,p1,p2); Picture p7 = smileys2(levels-2,p3,p2,p1,p4); return fourPics(clockwise270(p6),p5,clockwise180(p1),clockwise90(p7)); } }
public Picture smileys3(int levels, Color c1, Color c2, Color c3) { if (levels<=1) { return fourPics(patch(c1),patch(c1),smileyFace1(c2,c1),smileyFace2(c3,c1)); } else { return fourPics(smileys3(levels-1,c1,c2,c3), smileys3(levels-2,c2,c3,c1), smileys3(levels-3,c3,c2,c1), smileys3(levels-4,c3,c2,c1)); } }
eatRows
public void eatRows() { // For each row in the grid, eats the bagels on the side of the buggle // that has more bagels. // Does nothing if the number of bagels is the same on both sides. // Does not change the state of the buggle. if (isFacingWall()) { // base case eatRow(); } else { // general case eatRow(); // eat this row forward(); // go to next row eatRows(); // eat rest of the rows } }
eatRow
public void eatRow() { // Eats the bagels on the side of the buggle that has more bagels. // Does nothing if the number of bagels is the same on both sides. // Does not change the state of the buggle. int bagelsToLeft = countBagelsToLeft(); int bagelsToRight = countBagelsToRight(); // Compare the numbers, and behave appropriately if (bagelsToLeft > bagelsToRight) { eatBagelsToLeft(); } else if (bagelsToRight > bagelsToLeft) { eatBagelsToRight(); } else { // bagelsToRight == bagelsToLeft // Do nothing } }
The above version of eatRow() is defined in terms of the following auxiliary methods. These are not entirely necessary; their bodies could have been "inlined" into eatRow() instead. But they encapsulate solutions to subproblems of eatRow(), and are consistent with the goal of decomposing a solution to a big problem in terms of the solutions to subproblems. They also make it easier to read the code for eatRow(). Because of these sorts of advantages, we recommend solutions that consist of "lots of little methods".
public int countBagelsToLeft() { // returns the number of bagels to the left of (but not under) the buggle. // Does not change the state of the buggle. left(); int result = countBagels(); right(); return result; } public int countBagelsToRight() { // returns the number of bagels to the right of (but not under) the buggle. // Does not change the state of the buggle. right(); int result = countBagels(); left(); return result; } public void eatBagelsToLeft() { // Eats the bagels to the left of the buggle. // Assumes brush is up. Does not change the state of the buggle. left(); eatBagels(); right(); } public void eatBagelsToRight() { // Eats the bagels to the right of the buggle. // Assumes brush is up. Does not change the state of the buggle. right(); eatBagels(); left(); }Solving
countBagels
public int countBagels() { // returns the number of bagels IN FRONT OF (but not under) the buggle. // Does not change the state of the buggle. if (isFacingWall()) { return 0; } else { forward(); int subCount = countBagels(); // Count bagels from next cell to wall. int bagelInFront = bagelsUnder(); // Remember if there's a bagel in front. backward(); return subCount + bagelInFront; } }The above version of countBagels() uses the following bagelsUnder() auxiliary method:
public int bagelsUnder() { // returns the number of bagels under this buggle (0 or 1). // Does not change the state of the buggle. if (isOverBagel()) { return 1; } else { return 0; } }The bagelsUnder() method simplifies the handling of adding 0 or 1 within countBagels(). Without it, we would need to write something like one of the following two solutions:
// Alternative solution #1 to countBagels() that does not use bagelsUnder() public int countBagels() { // returns the number of bagels IN FRONT OF (but not under) the buggle. // Does not change the state of the buggle. if (isFacingWall()) { return 0; } else { forward(); int subCount = countBagels(); // Count bagels from next cell to wall. if (isOverBagel()) { backward(); return 1 + subCount; } else { backward(); return subCount; } } } // Alternative solution #2 to countBagels() that does not use bagelsUnder() public int countBagels() { // returns the number of bagels IN FRONT OF (but not under) the buggle. // Does not change the state of the buggle. int bagelsUnder = 0; if (isFacingWall()) { return 0; } else { forward(); int subCount = countBagels(); // Count bagels from next cell to wall. if (isOverBagel()) { bagelsUnder = 1; } backward(); return subCount + bagelsUnder; } }Alternative solution #1 involves a conditional that performs a test on the way out of the recursion. There is nothing wrong with this, but the two branches of the conditional are almost exactly the same, and it is nicer to capture the fact that they do the same thing except for the issue of adding 0 or 1. Alternative solution #2 requires initializing a local bagelsUnder variable to 0 and then changing it to 1 in certain circumstances. This requires an assignment statement (in this case, bagelsUnder = 1), a form of statement that we haven't even covered in class yet --- for good reason! It turns out that assignment statements can make it very difficult to reason about programs. As a general rule, assignment statements should be used sparingly, which is why alternative solution #2 is not recommended.
Solving
eatBagels
The following solution eats the bagels and leaves a mark on the way back out of the recursion rather than on the way into the recursion. It also focuses on eating the bagel in front of the buggle rather than on the one underneath the buggle. These two decisions simplify the method, especially the case where the buggle is facing a wall.
// Solution #1 to eatBagels() using paintCell() public void eatBagels() { // Eats the bagels in front of (but not under) the buggle. // Assumes brush is up. Does not change the state of the buggle. if (isFacingWall()) { // do nothing; } else { forward(); // Step forward to make problem smaller. eatBagels(); // Solve the subproblem. if (isOverBagel()) { // Test for bagel on way back out. pickUpBagel(); // if one is there, eat it paintCell(Color.red); // and mark the cell } backward(); // Step back to meet the invariant. } }// Solution #2 to eatBagels() not using paintCell() public void eatBagels() { // Eats the bagels in front of (but not under) the buggle. // Assumes brush is up. Does not change the state of the buggle. if (isFacingWall()) { // do nothing; } else { forward(); // Step forward to make problem smaller. eatBagels(); // Solve the subproblem. if (isOverBagel()) { // Test for bagel on way back out. pickUpBagel(); // if one is there, eat it brushDown(); // and mark the cell backward(); // when stepping back, brushUp(); // being sure to leave pen in up state. } else { backward(); // if no bagels, just step back. } } }