CSC151.01 2006S, Class 14: Recursion with Natural Numbers (1) Admin: * Reminder: CS Extra Tomorrow: Reports from the Field. * Homework 5 returned. Notes on the homework emailed. * Read "Writing Recursive Procedures" * Re-skim "Numeric Recursion" * I'll leave time for questions on homework 6. * Comment about G-Tones * Lab partners this week (randomly assigned) * Harrington and Rich * Thompson and Miller * McArdle and Howard * Wood and Zamora * Berg and Bonnin * LaRue and Rider * Cloninger and Cartagena * Brooks and An * McFarlin and Potthoff * McDonald and Pan * Brown and Mertes Overview: * Writing Recursive Procedures. * Homework 6 * Short Introduction to Numeric Recursion. * Lab. tally-skips * Find the number of times the symbol 'skip appears in a list of symbols (define tally-skips (lambda (lst) ... (define tally-skips (lambda (lst tally) * Design tip: If you think you're going to need an extra parameter, write a helper procedure! * How can we write tally-skips from scratch, without a helper? * Have I seen something similar before? * newsum, sum * difference (similar to sum, with - instead of +) * my-reverse * filter-out-negatives * filter-out-non-numbers * largest-of-list * longest-string * factorial * combine (similar to sum) * Which is similar * filter-out-negatives, because you can filter out the skips, rather than the negative numbers (define tally-skips (lambda (lst) (- (length lst) (length (filter-out-skips lst))))) * filter-out-negatives, because it deals with identifying special elements in a list * Suppose you don't find anything similar. What do you do? * What is a case simple enough that I should know the answer? * The empty list (define tally-skips (lambda (lst) (if (null? lst) * What should the answer be in this case? 0 (define tally-skips (lambda (lst) (if (null? lst) 0 * Detour: Make sure that the type of your base case matches * How can I simplify the parameter? * Typically for lists, we take the cdr (define tally-skips (lambda (lst) (if (null? lst) 0 (COMBINE lst (tally-skips (cdr lst)))))) * Suppose you have the answer for the recursive call * E.g., suppose we know how many times 'skip appeared in the cdr * What does that tell you about the overall result? * If the car is skip, add 1 to the recursive value * If the car is not skip, just take the recursive value (define tally-skips (lambda (lst) (if (null? lst) 0 (if (eq? 'skip (car lst)) (+ 1 (tally-skips (cdr lst))) (tally-skips (cdr lst)))))) (define tally-skips (lambda (lst) (if (null? lst) 0 (+ (if (eq? (car lst) 'skip) 1 0) (tally-skips (cdr lst)))))) (define tally-skips (lambda (lst) (cond ((null? lst) 0) ((eq? 'skip (car lst)) (+ 1 (tally-skips (cdr lst)))) (else (tally-skips (cdr lst)))))) "Agh. Why doesn't this just return 0 when it hits the empty list?" * Because not all of the operations are done when you hit the end > (tally-skips (list 'skip 'hop 'skip 'jump)) => (cond ((null? (list 'skip 'hop 'skip 'jump)) 0) ((eq? 'skip (car (list 'skip 'hop 'skip 'jump))) (+ 1 (tally-skips (cdr (list 'skip 'hop 'skip 'jump))))) (else (tally-skips (cdr (list 'skip 'hop 'skip 'jump))))))) FIRST TEST FAILS => (cond ((eq? 'skip (car (list 'skip 'hop 'skip 'jump))) (+ 1 (tally-skips (cdr (list 'skip 'hop 'skip 'jump))))) (else (tally-skips (cdr (list 'skip 'hop 'skip 'jump))))))) => (+ 1 (tally-skips (cdr (list 'skip 'hop 'skip 'jump)))) => (+ 1 (tally-skips (list 'hop 'skip 'jump))) NEITHER EMPTY NOR HAS A CAR OF SKIP => (+ 1 (tally-skips (cdr (list 'hop 'skip 'jump)))) => (+ 1 (tally-skips (list 'skip 'jump))) NOT EMPTY BUT BEGINS WITH SKIP => (+ 1 (+ 1 (tally-skips (cdr (list 'skip 'jump))))) => (+ 1 (+ 1 (tally-skips (list 'jump)))) NOT EMPTY DOES NOT BEGIN WITH SKIP => (+ 1 (+ 1 (tally-skips (cdr (list 'jump))))) => (+ 1 (+ 1 (tally-skips null))) => (+ 1 (+ 1 0)) => (+ 1 1) => 2 Other design strategy: Use a helper (to better match our own strategy (define tally-skips-helper (lambda (tally remaining-elements) (if (BASE-CASE-TEST) (DO-SOMETHING-TO tally) ; most typically, return it (tally-skips-helper (UPDATE tally) (SIMPLIFY remaining-elements))))) * We've added an extra parameter to keep track of a running computation * How do we know that it's simple enough that we're done? * When the list is empty, we've counted all the skips that were there before * What do we return then? * tally (define tally-skips-helper (lambda (tally remaining-elements) (if (null? remaining-elements) tally (tally-skips-helper (UPDATE tally) (SIMPLIFY remaining-elements))))) * Simplify using cdr (define tally-skips-helper (lambda (tally remaining-elements) (if (null? remaining-elements) tally (tally-skips-helper (UPDATE tally) (cdr remaining-elements))))) * How do we update the "computed so far" variable * In this case, add 1 if the car is 'skip and 0 otherwise (define tally-skips-helper (lambda (tally remaining-elements) (if (null? remaining-elements) tally (tally-skips-helper (if (eq? (car remaining-elements) 'skip) (+ 1 tally) tally) (cdr remaining-elements))))) /Numeric Recursion/ A lot like list recursion Except * Different test for base case * Was (null? lst) or (null? (cdr lst)) * Now (zero? val) or (= val 1) or (<= val 0) or ... * Simplify * Was (cdr lst) * Now (- val 1)