CSC151.02, Class 15: Repetition and Recursion Admin * Sorry about the delay posting homework 2. * Writeup 1 is now graded. * No reading. * Exam 1 distributed. Due a week from tomorrow. Overview: * Repetition * Recursion * Examples / Scheme * Lab How Sam Grades: * Oddly: Don't be bothered by low homework grades. Do be bothered by low exam grades. * On the computer: I will generally email your homework back to you with interspersed comments. Key Things from Writeup 1 * Postconditions are vague "The result is a number" * One of your goals when you write postconditions should be to specify the result as carefully as possible. * E.g., quadratic-root: root is a "zero of the function". That is, a*root*root + b*root + c = 0 * E.g., swap first two Input list: lst Output list: newlst Postconditions: (1) (list-ref newlst 0) = (list-ref lst 1) (2) (list-ref newlst 1) = (list-ref lst 0) (3) (list-ref newlst i) = (list-ref lst i) for all other reasonable i ---------------------------------------- Repetition Some standard components to algorithms: * Naming values (define) * Building subroutines/procedures (define ... lambda) * Naming input to a procedure (see above) * Sequence operations: * We know how to sequence conditionals * We also know how Scheme usually sequences: It evaluates the arguments to a procedure before it applies the procedure (mix (mix (sift flour) (measure sugar)) (mix (measure milk) (melt butter) (dump-lots-of vanilla))) * Conditionals: How to choose between things. (if ...) (cond ...) * Repetition * Scheme uses a strange (but nicely mathematical) technique for repetition: recursion * To solve a big problem, (1) remove part of the problem (2) solve the smaller problem (3) reuse the solution to the small problem in the solution to the big problem How is this helpful? Consider the problem of finding the length of a list. Suppose we need to write length. (define length (lambda (lst) (if (null? lst) 0 (+ 1 (length (cdr lst)))))) (length (list 1 2 3 4 5)) => (+ 1 (length (cdr (list 1 2 3 4 5)))) => (+ 1 (length (list 2 3 4 5))) => (+ 1 (+ 1 (length (list 3 4 5)))) => (+ 1 (+ 1 (+ 1 (length (list 4 5))))) => (+ 1 (+ 1 (+ 1 (+ 1 (length (list 5)))))) => (+ 1 (+ 1 (+ 1 (+ 1 (+ 1 (length (list))))))) => (+ 1 (+ 1 (+ 1 (+ 1 (+ 1 0)))))) => (+ 1 (+ 1 (+ 1 (+ 1 1)))) => (+ 1 (+ 1 (+ 1 2))) => (+ 1 (+ 1 3)) => (+ 1 4) => 5 Runaway recursion (define length (lambda (lst) (if (null? lst) 0 (+ 1 (length lst))))) (length (list 1 2 3 4 5)) => (+ 1 (length (list 1 2 3 4 5))) => (+ 1 (+ 1 (length (list 1 2 3 4 5)))) => (+ 1 (+ 1 (+ 1 (length (list 1 2 3 4 5))))) How to avoid "runaway" recursion: Always make sure that your argument is "smaller" in the recursive call to the same procedure. factorial n! = 1 * 2 * .... * n-1 * n E.g. 5! = 1 * 2 * 3 * 4 * 5 Most mathematicians say 0! is 1 n! is n*(n-1)!, for positive n In Scheme (define factorial (lambda (n) (if (zero? n) 1 (* n (factorial (- n 1)))))) Note that if you start this program with a negative n, it will run forever. Standard form of a recursive procedure in Scheme (define NAME (lambda (PARAMS) (if (SIMPLE PARAMS) SIMPLE-RESULT (EXPAND (NAME (SIMPLIFY PARAMS)))))) For factorial NAME: factorial PARAMS: n SIMPLE: zero? SIMPLE-RESULT 1 SIMPLIFY: subtract 1 EXPAND: multiply by n NAME: length PARAMS: lst SIMPLE: null? SIMPLE-RESULT: 0 SIMPLIFY: cdr EXPAND: add 1