CSC 151.01, Class 29: Pause for Breath (Recursion Review)
Overview
- Preliminaries
- Notes and news
- Upcoming work
- Extra credit
- Questions
- General review
- Key ideas from the reading
News / Etc.
- New partners!
- I’m back. I brought blue crab stickers and grapes.
- I apologize for my inconsistent responsiveness. Many of my conference days were long.
- It will take a few more days for me to get your exams back to you.
Rotating reminders
- Ask questions via email! I’m always happy to (try to) answer questions via email. There is no need to apologize when sending me questions. If I take too long to answer, send another email (or even text, if it’s a reasonable hour).
Upcoming Work
- HW 6 due tomorrow night.
- Lab writeup: TBD
- Reading for tomorrow: Numeric Recursion
- No homework over break.
- Friday’s quiz: Identify your classmates
Extra credit (Academic/Artistic)
- CS Table, Tuesday at noon, SIGCSE Debrief
- CS Extras, Thursday at 4:15 pm: ???
Extra credit (Peer)
You do not get extra credit for supporting yourself.
Extra credit (Misc)
Good things to do
Questions
- Can you help with question 1 on the homework?
- I would suggest writing a helper procedure that keeps track of where you are in the list and using direct recursion.
(define remove-every-kth-helper
(lambda (remaining pos-in-original-list k)
(cond
[(null? remaining) ...]
[(some-question-about pos) ...]
[else ...])))
(define remove-every-kth
(lambda (lst k)
(remove-every-kth-helper lst 0 k)))
- Can you help with question 2 on the homework?
- Sure. Here’s a solution
(define palindrome?
(lambda (str)
(string-=? str (list->string (string->list (reverse str))))))
- You said we couldn’t use
string-=?. You said that we should be more - efficient than that solution.
- Oh. Let’s try something else. We don’t want to create lots of intermediate things. We have to process lots of positions, we probably need recursion. We’ll keep track of the position.
(define palindrome?
(lambda (str)
(cond
[(not (string? str))
(error "I'm sorry, but it appears that you lack the general competence to understand what a string is. Non strings cannot be palindromes.")]
[else
(palindrome?-helper str 0))))
- Can we have conditions that do nothing?
- Sure. Just don’t put anything after the guard.
- Is daylight savings time bad for student awareness in the spring?
- Almost certainly. It’s bad for professor awareness in the spring.
- Do you have hints on problem 3?
- Three things to consider
- First, is the image large enough to do something with? If not, stop and return the image.
- Second, fill in the center portion of the image.
image-select-rectangle!with appropriate parameters, thenimage-fill!. (You might also addcontext-set-fgcolor!. You might usepaste-and-scale!`) - Third, recurse. You need to do eight recursive calls.
(define whatever-this-is-called!
(lambda (image left top width height small-enough)
(cond
[(small?)
image]
[else
(fill-in-the-center! ...)
(whatever-this-is-called! image newleft0 newtop0 newwidth newheight small-enough)
(whatever-this-is-called! image newleft0 newtop0 newwidth newheight small-enough)
(whatever-this-is-called! image newleft1 newtop1 newwidth newheight small-enough)
(whatever-this-is-called! image newleft2 newtop2 newwidth newheight small-enough)
(whatever-this-is-called! image newleft3 newtop3 newwidth newheight small-enough)
(whatever-this-is-called! image newleft4 newtop4 newwidth newheight small-enough)
(whatever-this-is-called! image newleft5 newtop5 newwidth newheight small-enough)
(whatever-this-is-called! image newleft6 newtop6 newwidth newheight small-enough)
(whatever-this-is-called! image newleft7 newtop7 newwidth newheight small-enough)])))
General Review
We learned about verifying preconditions when Sam was here.
We learned two new data types, characters and strings.
We learned about helper recursion, which is …
We learned about tail recursion, which is …
Basic recursion does recursion within the procedure.
Tail recursion uses a helper procedure.
We learned some things, but we still need more assistance in understanding them.
- Recursion: Idea that a procedure can call itself. We’re used to procedures calling other procedures; this is just an extension of that idea.
- Note: This recursion has to stop at some point. Hence, before calling itself, we should have the procedure decide whether or not to call itself and have a chance that it will decide not to. Base case test.
- Note: We help ourselves achieve the base case by simplifying the parameter (making it closer to the base case test) at each recursive call.
(define recursive-proc
(lambda (lst)
(if (null? lst)
2
(+ (recursive-proc lst) 2))))
(recursive-proc (list 1))
=> (+ (recursive-proc (list 1)) 2)
=> (+ (+ (recursive-proc (list 1)) 2) 2)
Three/four parts to each recursive procedure: base case test, base case result, recursive call with simplification.
Observation: Sometimes it is useful to keep track of something not given to us in the original procedure call. For example, if we want the largest element in the list, we might want to keep track of the largest element we’ve seen so far.
(define largest
(lambda (lst)
(largest-helper (cdr lst) (car lst))))
(define largest-helper
(lambda (lst largest-so-far)
(if (null? lst)
largest-so-far
(largest-helper (cdr lst) (max largest-so-far (car lst))))))
(define sierpinski-carpet
(lambda (image small-enough)
(siepinski-carpet-helper image 0 0 (image-width image) (image-height image) small-enough)))
(define sierpinski-carpet-helper
(lambda (image left top width height small-enough)
...))
If we are using husk-and-kernel strategy (and we should), we might also verify the preconditions in the original procedure. (Husk and kernel requires a helper. That helper may not need extra parameters.)
(define sum
(lambda (lst)
(cond
[(not (list? lst))
(error "...")]
[else
(sum-helper lst)])))
(define sum-helper
(lambda (lst)
(if (null? lst)
0
(+ (car lst) (sum-helper (cdr lst))))))
This is helper recursion. It is not tail recursion.
Tail recursion: When you finish the recursive call, do nothing else.
Is the following procedure tail recursive?
;;; Procedure:
;;; last
;;; Parameters:
;;; lst, a list [unverified]
;;; Purpose:
;;; Find the last element in a list
;;; Produces:
;;; last-element, a value
;;; Preconditions:
;;; lst is non empty [unverified]
;;; Postconditions:
;;; last-element = (list-ref lst (- (length ls) 1))
;;; Note:
;;; list-ref is expensive because it has to step through each element of
;;; the list until it reaches the right position. length is expensive
;;; for similar reasons. Hence, we want to try to step through the list
;;; only once in writing last.
(define last
(lambda (lst)
(if (null? (cdr lst)) ; if the list has only one element
(car lst)
(last (cdr lst)))))
Is this tail recursive?
- No: There is no helper procedure.
- Yes: You don’t need a helper procedure to be tail recursive. We don’t have additional work surrounding the recursive call to last.
Tail recursion is not the same as helper recursion
- You can be tail recursive but not use helper recursion. (tail)
- You can use helper recursion, but not be tail recursive (sum-helper above)
- How can we figure out how expensive a procedure is?
- Try to write it yourself.
- For procedures that work on lists, think about whether or not it has to look at every position/element of the list.