Copyright © Cay S. Horstmann 2010 - 2015
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.
Modified by:
Maze
that you can initialize with a maze such as 13 X 29
***************************** ** *** * ** *** * ******************** ** *** * * * ** *** * ******* ********** ** * ******************** ****** ******* ******* ****** ****** ******* ****** * ******* ******* ****** * **** ******* ** * * * ******* ******* ****** * **** ******* *** ****** ************** **************
The *
are walls, and the blank spaces are corridors. Each corridor is one space wide (horizontally or vertically).
Given a row and column, how can you tell whether you are at an exit of a given maze? (Using the Maze
class, of course.)
Robot
that lives in a maze. Make a project with these classes and this MazeSolver class.
Your job is to complete the recursive escape
method so that the robot escapes the maze and prints the trail.
Start with the base case:
What is the code of your escape
method so far?
You may assume the robot has been placed with a wall to the right when it was constructed. Now go on:
(There is no point turning right? Why?)
You may have lost the wall to the right. Make a sketch (in ASCII art) showing how this can happen.
canMove
). How can it check whether it has lost the wall (no wall to the right)?It must then again have a wall to the right. Why?
(You had to turn to see the wall.)
What is your code so far?
escape
so that it can escape from that position. That's your recursion. escape
method?MazeSolver
program?escape2
: Make the robot turn around in all four directions.
In each direction with a path emanating from it (i.e canMove
is true), the robot clones itself. Move the clone into the path (one step), and let it escape (call escape2()).
Complete the loop:
public boolean escape2()
{ if (atExit()) return true; for (int i = ...) // this loop makes the robot check all possible direction { turnRight(); if (...) { Robot cloned = clone(); cloned....(); if (cloned.escape2()) { visited... return true; } } } return false; }
What is your code?
What happens when you run the program? Remember to change the call in the MazeSolver
to escape2
escape2
without recursion?
Why or why not?
Let's try it with this method:
int triangleArea(int width) { if (width == 1) return 1; int smallerArea = triangleArea(width - 1); int area = smallerArea + width; return area; }
Make an index card like this:
triangleArea(width = 4) smallerArea = triangleArea(3) ?
Now put this one aside and make another one for the call to triangleArea(3)
. Put this one aside too, on the top of the preceding one.
Keep going:
triangleArea(width = 2) smallerArea = triangleArea(1) ?
When you reach triangleArea(1)
, the card looks different:
triangleArea(width = 1) return 1
Now find the card with the ?
next to triangleArea(1)
. Cross out the ?
and write the returned value 1
next to it. Now you can continue the method:
triangleArea(width = 2) smallerArea = triangleArea(1) = 1 area = 1 + 2 = 3 return 3
Do you have a card calling for triangleArea(2)
? It's got to be there somewhere. Cross out the ?
and write 3 next to it. Keep on going.
This activity should give you an idea of the stack of method calls that are pending as the recursion progresses.
What do you get for triangleArea(4)
when all is done?
int largest(int[] values, int start, int res) { if (start == values.length) return res; int res2 = Math.max(res, values[start]); return largest(values, start + 1, res2); }
Let's study the call
largest(new int[] { 3, 4, 1 }, 0, Int.MIN_VALUE)
Make a card
largest(values = [3, 4, 1], start = 0, res = Int.MIN_VALUE) res2 = ... return largest(values, ..., ...) ?
What did you fill in for the ...?
?
. We don't know the answer, so make a new card. What is your card? largest(values = [3, 4, 1], start = 3, res = ...) return ...
What is ...
?
You have just seen a “tail recursion”, in which the recursive call is the last item in the computation. These recursive calls can be automatically translated into loops, which gives you the best of both worlds—easy code and good performance.
RecursiveFib
class is from your text book.
Run the program with n = 50. What happens?
If you find it running too slowly, you can terminate it by clicking on the stop (red square) button of the console window.
fib(42) = 267914296 fib(43) = 433494437
But that's weird. Given these two values, it is trivial to compute fib(44)
. What is it? (Get out a calculator. Calculate 267914296+433494437
)
fib
method. Let's say we want to step through fib(40)
. But it is called in a loop. We don't want to click the "Step Over" button 40 times. That's where conditional breakpoints come in.
Make a breakpoint on line if (n <= 2)
. Right-click on it and select Breakpoint -> Properties. Click on “Break when hit count” and enter 40 after “equals to”.
Now select Run → Debug from the menu.
What happens?
If the program does not stop, you may not be in debug perspective. Look at the upper right. That shows thecurrent persective. If Debug is not selected, select it. Now try Run → Debug
long first = fib(n - 2);
Step into that call again. And keep clicking on the “Step Into” button. Watch the call stack (Window -> Debugging -> Call stack) and the variable display (Window -> Debugging -> Variables). Describe what you see. What is n
? What is on the call stack?
It would be pretty tedious to keep stepping in all the way to the base case, so set a break point on line 27 and click the Continue button
How many invocations of fib
are on the run time stack now?
return 1
line and set a breakpoint at the line
long result = first + second;
Click the Continue button (or hit the F5 key). What is the value of n
? What is the value that is about to be returned? (You may need to click on the Variables tab to see the values of n
, first
, and second
.)
n
, first
, and second
, that you encounter. Do this for at least ten values.In the previous section, you saw how to use the debugger for analyzing, and that is often the best strategy. It is, however, not the strategy that students use most commonly. Inexperienced programmers often sprinkle their code with print statements like these:
System.out.println("Yoohoo! I got this far!"); System.out.println(n);
Print statements can waste a lot of your time. You put them in, you take them out, you put them back in, you comment them out, you remove the comments, and when it all works you take them out for good. Until the next bug appears and you wish you had left them in.
In this section, you will practice the use of logging statements. Logging is easy. Instead of System.out.println, simply use Logger.getGlobal().info. For example,
Logger.getGlobal().info("Entering fib. n=" + n);
Logging is better than System.out.println for two reasons:
Logging can be better than using the debugger.
fib
method. At appropriate places, add the following logging statements:
Logger.getGlobal().info("Entering fib. n=" + n);;
Logger.getGlobal().info("Exiting fib. return=" + ...);;
What is the code for your fib
method?
main
method, add
Logger.getGlobal().setLevel(Level.ALL);
Run your program (changing the upper limit of the loop in the main
method to a small enough value). What is the output?
knownFibonacciValues
to the class. In the fib
method, first look up knownFibonacciValues[n]
. If it is non-zero, return it. Otherwise, compute it in the normal way. Just before returning, call knownFibonacciValues[n] = return value
so that it is know the next time.
What is the code of your method?
setLevel
call of the main
method to
Logger.getGlobal().setLevel(Level.OFF);
Run your program again. What happens?
Suppose you find another bug and want to turn logging back on. How do you do that?
In a small program, using Logger.getGlobal().info works fine. As your programs grow larger, you can control your logging in more sophisticated ways. You can use different logging levels. Instead of info, call methods severe for important messages or fine for "fine-grained" messages. Then call the setLevel method to set the level of the messages that you want to see in a particular program run. You can also define your own logger objects in addition to Logger.global. Keep this in the back of your mind; it will come in handy in the future.