CS51A - Spring 2019 - Class 16

Example code in this lecture

   mystery_recursion.py
   turtle_recursion.py

Lecture notes


  • the four steps to writing a recursive function:
       1. define what the function header is
       2. define the recursive case
       3. define the base case
       4. put it all together

  • What does mystery_recursion function in mystery_recursion.py code do?
       - Recursive function
       - Work through a small example:
          - rec_mystery([2, 4, 3, 1])
       
          rec_mystery([2, 4, 3, 1]) # when recursive call finishes: compares m = 4 and l[0] = 2 and returns 4
          |
          |
          ---> rec_myster([4, 3, 1]) # when recursive call finishes: compares m = 3 and l[0] = 4 and returns 4
             |
             |
             -->rec_mystery([3, 1]) # when recursive call finishes: compares m = 1 and l[0] = 3 and returns 4
                |
                |
                --> rec_mystery([1]) # returns 1
       
       - what does this function do?
          - calculates the max!

       - how?
          1. rec_max(list)

          2. rec_max(list) = ??? rec_max(list[1:])
             - assume/trust that the recursive call works
             - if it does, then it will return the largest value in list[1:]
             - the largest value of the whole list is then either the first element (l[0]) or the largest value in the rest of the list (rec_max(list[1:])
          3. The list will get smaller and smaller. max([]) doesn't really make sense, so our base case will be when there's a single element

          
       - recursive case:
          - make a recursive call on the rest of the list
          - store that value in m
          - compare m to the first element and return whichever it larger
       

  • look at the spiral function in turtle_recursion.py code
       - for example, what would the picture look like if I called:
          >>> spiral(80, 50)
       - what does this function do?
          - recursive function
          - draws a spiral on the screen
             - forward 80
             - left 30
             - spiral( 76, 49 )
                - forward 76
                - left 30
                - spiral(72.2, 48)
                   - forward 72.2
                   - left 30
                   - ...
          - when does it stop?
             - when levels == 0
                - I put a dot here just do make it explicit
                - we could have also just done nothing if we wanted
          - repeat 50 times:
             - forward length
             - left 30
             - reduce length by 5%

       - what if we wanted to end up back at the starting point, but we couldn't pick the pen up?
          - one way would be to trace our steps backward
          - Assume that the recursive call returns back to its starting point. What would we need to do to make sure that our call returned back to the starting point?
             - Add the following after the recursive call:

                right(30)
                backward(length)

          - if we run it now, we draw the spiral all the way down, and then we retrace backwards
       - why does this work?
          - each call to spiral retraces its own part after the recursive call
          - the stack keeps track of each of the recursive calls

  • run broccoli_demo function in turtle_recursion.py code
       1. define what the function header is
          - broccoli(x, y, length, angle)

       2. define the recursive case
          - broccoli is a line with three other broccolis at the end
             - one directly straight out
             - one 20 degrees to the left
             - one 20 degrees to the right
          - the three other broccolis should be smaller/shorter than the current

       3. define the base case
          - eventually the length of the broccoli to be drawn gets too short
          - at that point instead of recursing, we draw a yellow dot at the end and stop recursing

       4. put it all together
          - look at broccoli function in turtle_recursion.py code
             - draw a line in the direction specified
             - check the base case
                - if the line length is less than 10, just put a yellow dot at the end
             - otherwise, it's the recursive case
                - draw three smaller broccolis at different angles

             - what are new_x and new_y?
                - the ending coordinates of the line being drawn
             - why do we need to save them?
                - after the first recursive call to broccoli the turtle won't be in the same place

       - if we turn tracing back on, what will we see, that is, what order will it draw in?
          - going to go right (angle -20) over and over again until it gets too short
             - it will end the recursion then draw a short center
             - this also will be a base case and draw the left
          - then it will start to work it's way back up
          - eventually, it will make it back up to the top-level and start drawing the center stalk
          - after drawing the center stalk, it will draw the left stalk

  • Why does recursion work?
       - Specifically, why can we trust that the recursive call is going to work?
          - The base case works (assuming we coded it up correctly)
          - If the base case works, the the call before the base case works
          - If that works, then the call before that works.
          - Etc, etc.