CS 227 Lecture -*- Outline -*- * Procedural Abstraction of Deep Recursion (7.5) (can skip if pressed for time, as similar to 7.4) WARNING (to students): this is not the same as in the book! it's better! ** The pattern Like flat recursion, tree (or deep) recursion has a standard program pattern. Q: What was the pattern, in words, for recursion over tree of symbols? from section 4.3 and 4.4: [base] Check if the argument is null [flat] Check if car of argument is a symbol combine the car with recursion on the cdr. [doubly] If car of argument is not a symbol, recurse on both the car and cdr *** filter-in-all-c The idea is to make a copy only retaining those elements that pass the predicate. --------------------- ((filter-in-all-c odd?) '((4 5) 1)) ==> ((5) 1) ((filter-in-all-c even?) '(3 () 2)) ==> (() 2) ; TYPE: (-> ((-> (T) boolean)) (-> ((tree T)) (tree T))) ; assume T is an atomic-item ;; compare Program 7.25 (define filter-in-all-c (lambda (pred) (letrec ((helper ; TYPE: (-> ((tree T)) (tree T)) (lambda (tr) (cond ((null? tr) '()) ((atomic-item? (car tr)) (if (pred (car tr)) (cons (car tr) (helper (cdr tr))) (helper (cdr tr)))) (else (cons (helper (car tr)) (helper (cdr tr)) )))))) helper))) ----------------- **** using let Q: how (else) could you improve the time efficiency of filter-in-all-c ? A: use let to keep from repeating (car tr) ---------------------- ;; compare Program 7.25 (define filter-in-all-c (lambda (pred) (letrec ((helper ; TYPE: (-> ((tree T)) (tree T)) (lambda (tr) (if (null? tr) '() (let ((a (car tr))) (if (atomic-item? a) (if (pred a) (cons a (helper (cdr tr))) (helper (cdr tr))) (cons (helper a) (helper (cdr tr)))) ))))) helper))) ----------------- *** sum-all --------------------- (sum-all '(3 (1 4) ((2)))) ==> 10 (sum-all '(3 () 2)) ==> 5 ; TYPE: (-> ((tree number)) number) ;; compare Program 7.27, pg. 225 (define sum-all (letrec ((helper ;TYPE: (-> ((tree number)) number) (lambda (tr) (if (null? tr) 0 (let ((a (car tr))) (if (number? a) (+ a (helper (cdr tr))) (+ (helper a) (helper (cdr tr)))) ))))) helper)) --------------------- As in filter-in-all-c, the number? test makes this work properly on the second example, and avoids adding () to a number. We wrote this with a letrec to make it look more like sum-all ** abstracting the pattern common parts and the abstracting lambda --------------------- (define deep-recur-abs (lambda (_____________) (letrec ((helper (lambda (tr) (if (null? tr) ________ (let ((a (car tr))) (if (______ a) __________________ __________________)) )))) helper))) --------------------- Will need 4 parameters to abstract out the varying parts: seed, a procedure to tell us whether the car is a leaf, a procedure for combining the car and recursion on the cdr, flat-proc, and a procedure for combining the results of the double recursion, tree-proc. ------------- (define deep-recur-abs ; TYPE: (-> (T (-> (S T) T) (-> (T T) T) ; (-> (datum) boolean)) ; (-> ((tree S)) T)) (lambda (seed flat-proc tree-proc leaf?) ; REQUIRES: (leaf? x) = (x has type S) ; ENSURES: (result '()) = seed, ; (result (cons x t)) ; = (flat-proc x (result t)), ; (result (cons t1 t2)) = ; (tree-proc (result t1) (result t2)) ; where (leaf? x) holds, ; but not (leaf? t1) and (leaf? t2). (letrec ((helper ; TYPE: (-> ((tree S)) T) (lambda (tr) (if (null? tr) seed (let ((a (car tr))) (if (leaf? a) (flat-proc a (helper (cdr tr))) (tree-proc (helper a) (helper (cdr tr))))) )))) helper))) --------------- Q: How would you write sum-all using deep-recur-abs? Q: How would you write filter-in-all-c using deep-recur-abs? ** comparsion with deep-recur in the book (omit if no time) The program in the book is like the following Note that it does not have the leaf? parameter... that's hard-wired into the code. Q: can you curry deep-recur-abs to prepare for specialization of leaf? (define deep-recur-abs-m ; TYPE: (-> ((-> (datum) boolean)) ; (-> (T (-> (S T) T) (->(T T) T)) ; (-> ((tree S)) T))) (lambda (leaf?) (lambda (seed flat-proc tree-proc) (deep-recur-abs seed flat-proc tree-proc leaf?)))) Now we can write the program in the book using deep-recur-abs-m (define deep-recur ; TYPE: (-> (T (-> (S T) T) (-> (T T) T)) ; (-> ((tree S)) T)) ; REQUIRES: if x has type S, then (atomic-item? x) is true (deep-recur-abs-m atomic-item?)) The program in the book is basically this --------------------- ;; compare Program 7.28, pg. 227 (define deep-recur ;TYPE: (-> (T (-> (S T) T) (-> (T T) T)) ; (-> ((tree S)) T) (lambda (seed item-proc list-proc) (letrec ((helper ; TYPE: (-> ((tree S)) T) (lambda (ls) (if (null? ls) seed (let ((a (car ls))) (if (atomic-item? a) (item-proc a (helper (cdr ls))) (list-proc (helper a) (helper (cdr ls))))) )))) helper))) --------------------- Suppose we want to deal with trees of ratls ------------- DEEP-RECUR vs. DEEP-RECUR-ABS (let ((sum-all (deep-recur 0 + +))) (sum-all (list (make-ratl 3 4) (make-ratl 2 3)))) ==> 12 ;; want: 17/12 = 3/4 + 2/3 ;; got: 12 = 3 + 4 + 2 + 3 ------------- The problem is that deep-recur thinks that 3, 4, 2, and 3, are the leaves, but that is not right. Need to pass leaf test as a parameter! actually the above has no hope of being right, since it doesn't make sense in terms of types (tree of ratl) but if try to fix this... ------------- (rprint (let ((sum-rat-all (deep-recur (make-ratl 0 1) r+ r+))) (sum-rat-all (list (make-ratl 3 4) (make-ratl 2 3)))) ;; signals an error ------------------ Suppose ratl provides a procedure ratl? ------------------ assume ratl?: (-> (datum) boolean) (rprint (let ((sum-rat-all (deep-recur-abs (make-ratl 0 1) r+ r+ ratl?))) (sum-rat-all (list (make-ratl 3 4) (make-ratl 2 3))))) =prints=> 17/12 --------------