When circumstances allow, I'll be typing up bits of my lecture notes and posting them online. These may or may not bear any resemblance to the actual lectures.
In class we'll cover only flowcharts, which describe the "flow of control" in a computer program. Using a flowchart, you can diagram what choices the computer has at each decision point (if or ifelse statement). A flowchart can include as much or as little detail as you need.Below is an example of a simple flowchart describing the process of waking up in the morning:
Notice that actions are described in rectangular boxes, decisions appear in diamond shaped boxes, and the start and end are in circles. Arrows indicate the "flow of control" in the program. (I don't know what the green bullet shape is for.) We'll display events in which the computer reads or displays something in parallelogram shaped boxes, as you can see from this flow chart describing a familiar quiz program.
When a procedure is used as its own helper procedure in a program, we call that program recursive. Recursive programs are often the best way to apply functional programming techniques to a problem.
Recall our example of using Newton's Method to find the zeros of a function. Let's use it to estimate ln(4) by finding the value of x for which ex - 4 = 0.
to function :value output (exp :value) - 4 end to derivative :value output exp :value end to improve_guess :guess output :guess - (function :guess) / (derivative :guess) endOnce we've copied and pasted this little program into Logo, we can estimate ln(4) as follows:
? print improve_guess 2 1.54134113294645 ? print improve_guess 1.5 1.39252064059372 ? print improve_guess 1.4 1.38638785576643 ? print improve_guess 1.386387 1.38629436541074The values output by Logo have mostly stopped changing, so this answer is probably right. We can test it by plugging it in to procedure function and seeing if we get 0.
? print function 1.38629 -1.74444415237396e-005 ?Yup, looks good!
That was annoying. The work of calculating the function and its derivative can't be avoided, but Logo should be able to repeat procedure improve_guess without our having to type it in a bunch of times.
Instead of typing improve_guess at the keyboard a large number of times we will use if and recursion to stop the procedure when our guess is improved to the desired accuracy. We'll do this by making changes to the improve_guess procedure to make it keep working until the guess is good enough.
How do we want to change improve_guess?
Instead of just outputting a guess, we want it to check to see if the guess is good enough. If it isn't, we want to improve it. If it is good enough, we want to output it. (You might think we'd want to print it, but that's not a good idea if we want to use this program for other tasks later.)
We come up with something like:
to improve_guess :guess if (good_enough :guess) output :guess output improve_guess :guess - (function :guess) / (derivative :guess) endThis procedure requires a helper procedure (predicate) good_enough, which looks like:
to good_enough :guess output lessp abs function :guess .01 end(Draw a plumbing diagram for output lessp abs function :guess .01. What does it do? Will this work?)
We can now test procedure improve_guess. If it doesn't work, we'll have to debug it. If it works, we can think about writing a nice, chatty procedure that asks for a guess and prints the solution. We could also think about ways of stopping improve_guess if Newton's method cannot find a solution, and about ways of allowing the user to type in a function as well as a guess.
to maze.game print [You are lost in a cave. Will you go left or right?] if equalp readword "right [print [You are saved!] stop] maze.game endThis is an example of a recursive procedure. Recursive procedures have the following two properties:
Recursive definitions are even more closely related to recursive procedures.Here's a recursive definition of n!, the factorial function:
to factorial :number if equalp :number 0 [output 1] output :number * factorial (:number - 1) endWhat's the stop condition? Where is the recursive call? Is this procedure an operation or command? What else do you notice about this procedure?
The procedure below accepts a length as input and draws a spiral shape whose longest leg is that length.
to spiral :length if lessp :length 1 [stop] forward :length right 60 spiral (:length - 10) endWhat is the stop condition? Where is the recursive call in spiral?
Play around with the spiral procedure -- what happens if you change the 60 or the 10? What happens if you change the order the instructions appear? Why?
Next, see if you can write a procedure that takes a number as input and displays a countdown from that number to 0. Use the spiral procedure as your guide. If you are successful, see if you can write a procedure to count up from 0 to the number input.
Can you write a Logo procedure that accepts a list as input and displays each member of the list alone on the screen? The Logo primitives first, butfirst and emptyp may be helpful here, or you might use count and item.
Next, see if you can write a maze game that "keeps score" -- each time the user makes a turn without escaping, add one to the score. When the player escapes, print their final score.
to countdown.recursive :number print :number countdown.recursive (:number-1) endUse the Logo menu to stop Logo. What went wrong?
Page 137 of Chapter 7 discusses the need for a stop rule that detects when a program has finished. In the case of countdown.recursive, you want to stop when the count reaches zero. Since your input might not be a positive integer, it's a good idea to play it safe and stop when the count is less than or equal to zero:
to countdown.recursive :number ifelse (greaterp :number 0)~ [print :number countdown.recursive (:number-1) ]~ [print "Blastoff!] endNote that you have to check to see if it's time to stop before the procedure runs itself as a helper procedure. Otherwise it goes into an infinite loop and doesn't stop to check if it's done. The procedure below provides an example of this common mistake -- save your work before running it.
to countdown.recursive2 :number print :number countdown.recursive2 (:number-1) if (lessp :number 1) [print "Blastoff! stop] end
to writers.quiz print [Who is the greatest writer ever?] if equalp readlist [Tolkien] [print [Yes, that's right!] stop] print [No, silly, it's Tolkien] endHere Logo's stop command is used to end the command writers.quiz before the instruction print [No, silly, it's Tolkien] is run. Try removing the stop command, and see what happens. stop is a command which forces the procedure it's in to quit. The textbook has an example of the use of stop to quit early in a procedure which uses the quadratic formula. In countdown.recursive2 we used it to stop a recursive procedure. In most other situations, it's best to try to avoid using stop. We used an if command to see if the countdown was finished. We could also have used ifelse, as in the example in the textbook.
to countdown.recursive3 :number ifelse (greaterp :number 1)~ [print :number countdown.recursive3 (:number-1) ]~ [print "Blastoff!] endIf you do the extra credit game project, you may find that the last few lines of some procedure are run over and over when the program completes. This is usually caused by using if rather than ifelse at a decision point. Remember that when the the if statement completes, the computer will move on to run the next line of Logo code, even if you don't want it to! Experiment with procedure brokenmaze below to see what can happen. Can you fix brokenmaze so that it only prints At last! You escaped! once when you run it?
to brokenmaze print [You are lost in a maze. Which way will you turn?] if equalp readword "left [brokenmaze] print [At last! You escaped!] endSummary: A recursive procedure is one that uses itself as a helper procedure. Every recursive procedure must have a stop condition (to prevent infinite loops) and a self-reference. Errors in the flow of control of a recursive procedure can cause infinite loops or other strange behaviors. See Chapter 7 for more examples of recursive procedures.