CSC151 2007S, Class 28: Naming Local Procedures * I am continuing to reserve our normal start-of-class time for comments on campus events. Admin: * Are there questions on Exam 2? * Tomorrow I leave for my conference after class, and won't have email access until Wednesday afternoon (or later). Please get questions to me before class! * Reading for tomorrow: Numeric Recursion. * Visitor at start of class tomorrow. Overview: * Why have local procedures. * Creating local procedures with letrec. * Creating local procedures with named let. * An example: reverse. * Lab! (finish on your own) =Exam Questions= * In image-quadrachorome, what should the definition look like: (define i-q (lambda (image e1 e2 e3 e4) (image-variant image ____))) ==Recent Activities in 151== * Learned recursion * Patterns of recursion: * Counting/tally * all-? any-? * Select/filter * ... * Helper recursion * Create a helper procedure that takes an extra parameter ("so-far") * Build the result as you go * E.g., difference * Return to preconditions * Part of the documentation: * Describes valid/invalid parameters * Recent idea: You can put in tests to check preconditions * Iowa's contribution to programming * Husk and Kernel * The husk tests the preconditions and stuff * The kernel does the stuff and stuff w/o testing preconditions * In corn, the husk is on the outside and the kernel is on the inside ==Today: Thinking more about H&K== * If it's done correctly, NO ONE SHOULD BE ABLE TO CALL THE KERNEL DIRECTLY * An example: rev, which reverses a list ;;; Preconditions: ;;; Parameter must be a list (define rev (lambda (lst) (cond ((not (list? lst)) (throw "Perhaps there was a bit of a misunderstanding, but that's not a list: " lst)) (else (rev-kernel lst))))) (define rev-kernel (lambda (lst) (if (null? lst) null (append (rev-kernel (cdr lst)) (list (car lst)))))) How do we protect the kernel? We normally protect things with let (define myrev (let ((myrev-kernel (lambda (lst) (if (null? lst) null (append (myrev-kernel (cdr lst)) (list (car lst))))))) (lambda (lst) (cond ((not (list? lst)) (throw "Perhaps there was a bit of a misunderstanding, but that's not a list: " lst)) (else (myrev-kernel lst)))))) ANother variant of reverse (define chris (lambda (lst) (cond ((not (list? lst)) (error "Chris does not like " lst)) (else (colonel-chris null lst))))) (define colonel-chris (lambda (reversed-so-far remaining) (if (null? remaining) reversed-so-far (colonel-chris (cons (car remaining) reversed-so-far) (cdr remaining))))) Very standard pattern of writing recursive helpers: * One helper/kernel * Called with some easily computed set of values Scheme provides an alternate to letrec for these examples "The named let" (define kris (lambda (lst) (cond ((not (list? lst)) (error "It looks like David's grade is")) (else (let kernel ((reversed-so-far null) (remaining lst)) (if (null? remaining) reversed-so-far (kernel (cons (car remaining) reversed-so-far) (cdr remaining)))))))) ==A Puzzle== What's the difference between let and letrec * The easy answer: You need letrec for recursive procedures But WHY? (let ((name exp) (name exp) ... (name exp)) ...) Names are not bound until AFTER evaluation (let ((rec (lambda (...) ... (rec ...))) (name exp) ... (name exp)) ...) But we needed rec bound so that we could use it. letrec does the magic to get the binding right. let* still doesn't bind until after the entry (rather than after all of the entries)