To understand recursion, one must first understand recursion. RECURSIVE STRUCTURE: Sub-problem must resemble the original problem Tree -> subtrees; List -> sublists; Expressions -> subexpressions (transformations: traversals, evaluation, code generation, …) E.g., f : EXPR -> int CORRECT DECOMPOSITION: (f ‘(+ e1 e2)) -> (+ (f ‘e1) (f ‘e2)) WRONG FORMULATION: -> (f ‘(e1 e2)) Requires f to handle expressions, plus unnessary inputs such as lists of expressions, empty list, etc. Creates unnecessary work with complicated logic ---------------------------------------------------------------------------------------------------------- While explaining program code, focus is on problem solving and not on data structure creation, low-level manipulation, and destruction. => DECLARATIVE PROGRAMMING cs3180(Prasad) L14Recur+Clojure
Specifying Incremental Work D1 1 -> f(1) ... n -> f(n) Dn n+1 -> f(n+1) cs3180(Prasad) L14Recur
Divide and Conquer Problem decomposition Subproblem1 Subproblem2 primitive recursion Subsolution1 Subsolution2 composition Solution cs3180(Prasad) L14Recur
Cartesian Product > (cart2 '(1 2) '(a b)) ( (1 a) (1 b) (2 a) (2 b) ) > (cart2 '(0 1 2) '(a b)) ( (0 a) (0 b) (1 a) (1 b) (2 a) (2 b) ) > (couple ' 1 '(b c)) ( (1 b) (1 c) ) > (couple ' 1 '(a b c)) ( (1 a) (1 b) (1 c) ) cs3180(Prasad) L14Recur
Divide and Conquer (cart2 '(a b) '(1)) car/cdr (couple ' a '(1)) (cart2 '(b) '(1)) “primitive” recursion ((a 1)) ((b 1)) append ((a 1) (b 1)) cs3180(Prasad) L14Recur
Different Problems; Same pattern cs3180(Prasad) L14Recur
Scheme Definition (define (cart2 x y) (if (null? x) '() (append (couple (car x) y) (cart2 (cdr x) y) ) (define (couple a y) (if (null? y) '() (cons (list a (car y)) (couple a (cdr y)) cs3180(Prasad) L14Recur
Alternate Syntax : lambda (define cart2 (lambda (x y) (if (null? x) '() (append (couple (car x) y) (cart2 (cdr x) y) ) (define couple (lambda (a y) (if (null? y) '() (cons (list a (car y)) (couple a (cdr y)) cs3180(Prasad) L14Recur
Clojure Syntax (defn couple [a y] (if (empty? y) '() (cons (list a (first y)) (couple a (rest y)) ) ) ) (defn cart2 [x y] (if (empty? x) '() (concat (couple (first x) y) (cart2 (rest x) y) (couple ' (1) '(a ("abc") c)) (((1) a) ((1) ("abc")) ((1) c)) (cart2 ' (1 2 3) '(a b c)) ((1 a) (1 b) (1 c) (2 a) (2 b) (2 c) (3 a) (3 b) (3 c)) cs3180(Prasad) L14Recur
Powerset > (powerset '(2)) ( ( ) (2) ) > (powerset '(1 2) ) ( ( ) (2) ) > (powerset '(1 2) ) ( (1) (1 2) () (2) ) > (powerset '(0 1 2)) ( (0 1) (0 1 2) (0) (0 2) (1) (1 2) () (2) ) Subsets of {0,1,2} with 0 are equinumerous with subsets of {0,1,2} without 0. cs3180(Prasad) L14Recur
(define (powerset s) (if (null? s) '(()) (append (ins (car s) (powerset (cdr s)) ) )) (define (ins a ss) (if (null? ss) '() (cons (cons a (car ss)) (ins a (cdr ss)) > (powerset '(0 1 2)) ((0 1 2) (0 1) (0 2) (0) (1 2) (1) (2) ()) (define (powerset s) (if (null? s) (list ()) (let ( (aux (powerset (cdr s))) ) (append (ins (car s) aux) aux ) )) cs3180(Prasad) L14Recur
Alternate Syntax : let (define (powerset s) (if (null? s) '(()) (let ( (aux (powerset (cdr s))) ) (append (ins (car s) aux) aux ) )) (define (ins a ss) (if (null? ss) '() (cons (cons a (car ss)) (ins a (cdr ss)) > (powerset '(0 1 2)) ((0 1 2) (0 1) (0 2) (0) (1 2) (1) (2) ()) (define (powerset s) (if (null? s) (list ()) (let ( (aux (powerset (cdr s))) ) (append (ins (car s) aux) aux ) )) cs3180(Prasad) L14Recur
Clojure Syntax (defn ins [a ss] (if (empty? ss) '() (cons (cons a (first ss)) (ins a (rest ss)) ) )) (defn powerset [s] (if (empty? s) '(()) (let [aux (powerset (rest s))] (concat (ins (first s) aux) aux > (powerset '(0 1 2)) ((0 1 2) (0 1) (0 2) (0) (1 2) (1) (2) ()) cs3180(Prasad) L14Recur
Related problems; Different Patterns cs3180(Prasad) L14Recur
Remove-First-TopLevel (define (rm-fst-top sym lis) (if (null? lis) '() (if (eq? sym (car lis)) (cdr lis) (cons (car lis) (rm-fst-top sym (cdr lis)) ) > (rm-fst-top 'a '(b (b a) a b a)) = (b (b a) b a) Linear recursion cs3180(Prasad) L14Recur
Alternate Syntax : if => cond (define (rm-fst-top sym lis) (cond ( (null? lis) '()) ( (eq? sym (car lis)) (cdr lis) ) ( else (cons (car lis) (rm-fst-top sym (cdr lis)) ) > (rm-fst-top 'a '(b (b a) a b a)) = (b (b a) b a) Linear recursion cs3180(Prasad) L14Recur
Remove-All-TopLevel Linear recursion (define (rm-all-top sym lis) (cond ( (null? lis) '()) ( (eq? sym (car lis)) (rm-all-top sym (cdr lis) ) ) ( else (cons (car lis) (rm-all-top sym (cdr lis))) ) > (rm-all-top 'a '(b (b a) a b a)) = (b (b a) b) > (rm-all-top ' (b a) '(b (b a) a b a)) = (b (b a) a b a) Linear recursion cs3180(Prasad) L14Recur
Remove-All-Expression-TopLevel (define (rm-all-top exp lis) (cond ( (null? lis) '()) ( (equal? exp (car lis)) (rm-all-top exp (cdr lis) ) ) ( else (cons (car lis) (rm-all-top exp (cdr lis)))) ) > (rm-all-top ' (b a) '(b (b a) a b a)) = (b a b a) Linear recursion cs3180(Prasad) L14Recur
Remove-All > (rm-all ' a '(b (b a) a b a)) = (b (b) b) (define (rm-all sym ll) (cond ( (null? ll) '()) ( (symbol? (car ll)) (if (eq? sym (car ll)) ( rm-all sym (cdr ll) ) ( cons (car ll) (rm-all sym (cdr ll)) ) ) ) (else (cons (rm-all sym (car ll)) (rm-all sym (cdr ll)) > (rm-all ' a '(b (b a) a b a)) = (b (b) b) Double recursion cs3180(Prasad) L14Recur
rm-all : Structural recursion Empty list (Basis case) (rm-all 'a '()) First – Atom (Recursive case) (rm-all 'a '(a b c a)) (rm-all 'a '(b b c a)) First - Nested list (Recursive case) (rm-all 'a '((a b c) d (a))) (rm-all 'a '(b (a b c) d (a))) cs3180(Prasad) L14Recur
Insertion sort (Note: creates a sorted copy) (define (insert n lon) (cond ((null? lon) (list n)) ((> n (car lon)) (cons (car lon) (insert n (cdr lon)))) (else (cons n lon)) ) (define (ins-sort lon) (if (null? lon) '() (insert (car lon) (ins-sort (cdr lon))) Precondition: second arg to insert ordered. Postcondition: insert returns an ordered list. cs3180(Prasad) L14Recur
Clojure Syntax (defn insert [n lon] (cond (empty? lon) (list n) (> n (first lon)) (cons (first lon) (insert n (rest lon))) :true (cons n lon)) ) (defn ins-sort [lon] (if (empty? lon) '() (insert (first lon) (ins-sort (rest lon))) Precondition: second arg to insert ordered. Postcondition: insert returns an ordered list. > (ins-sort '(2 3 5 4 1 0)) (0 1 2 3 4 5) cs3180(Prasad) L14Recur
Subset (uses OR and AND instead of IF) (define (subset? ss s) (or (null? ss) (and (member (car ss) s) (subset? (cdr ss) s) ) ) ) 1=> (subset? '(a b c) '(A p 1 C 2 B q r)) #t 2=> (subset? '(a b c) '(p)) #f cs3180(Prasad) L14Recur
Anonymous functions and list membership test in Clojure > ( (fn [x y] (+ x y)) 25 30) 55 > ( #(+ %1 %2) 25 30) > (some #(= 5 %) '(5 30)) true > (some #(= 5 %) '(15 30)) nil ```’’’’’ cs3180(Prasad) L14Recur
Subset in Clojure (cf. case sensitive Scheme) (defn subset? [ss s] (or (empty? ss) (and (some #(= (first ss) %) s) (subset? (rest ss) s) ) ) ) > (subset? '() '(A p 1 C 2 B q r)) true > (subset? '(a b c) '(A p 1 C 2 B q r)) nil > (subset? '(a b c) '(p a c b)) > (defn in? "true if seq contains elm" [seq elm] (some #(= elm %) seq)) #'sandbox20469/in? > (in? '( 1 2 3) 1) true > (in? '( 1 2 3) 4) nil cs3180(Prasad) L14Recur
Expression evaluation : A simple syntax directed translation expr -> x | y | z expr -> (+ expr expr) expr -> (if expr expr expr) Write a recursive definition for a function ops that counts the number of “+”s. cs3180(Prasad) L14Recur
; e is assumed to be a symbol or a list (cond ((symbol? e) 0) (define (ops e) ; e is assumed to be a symbol or a list (cond ((symbol? e) 0) ((eq? (car e) '+) (+ 1 (ops (cadr e)) (ops (caddr e))) ) ((eq? (car e) 'if) (+ (ops (cadr e)) (ops (caddr e)) (ops (cadddr e)))) (else (display 'ILLEGAL)) ) (ops 'x) (ops ' (+ y z)) (ops ' (if x (+ y z) (+ x z))) cs3180(Prasad) L14Recur
(defn third [x] (second (rest x))) (defn ops [e] ; Clojure code "e is assumed to be a symbol or a list" (cond (symbol? e) 0 (= (first e) '+) (+ 1 (ops (second e)) (ops (third e)) ) (= (first e) 'if) (+ (ops (second e)) (ops (third e)) (ops (last e))) true 'ILLEGAL )) (defn third [x] (second (rest x))) (defn fourth [x] (third (rest x))) (ops 'x) (ops ' (+ y z)) (ops ' (if x (+ y z) (+ x z))) cs3180(Prasad) L14Recur
; e is assumed to be a symbol or a list (if (member e '(x y z)) 0 (Alternative Scheme syntax with member and case-construct (not equivalent)) (define (ops e) ; e is assumed to be a symbol or a list (if (member e '(x y z)) 0 (if (symbol? e) (display 'ILLEGAL) (case (car e) ((+) (+ 1 (ops (cadr e)) (ops (caddr e))) ) ((if) (+ (ops (cadr e)) (ops (caddr e)) (ops (cadddr e))) ) (else (display 'ILLEGAL))) ) )) (ops 'x) (ops ' (+ y z)) (ops ' (if x (+ y z) (+ x z))) cs3180(Prasad) L14Recur
(ops 'x) 0 (ops '(+ y z)) 1 (ops '(if x (+ y z) (+ x z))) 2 Examples (ops 'x) 0 (ops '(+ y z)) 1 (ops '(if x (+ y z) (+ x z))) 2 cs3180(Prasad) L14Recur
(Alternative syntax in Clojure (not equivalent or robust)) (defn ops [e] ;e is assumed to legal (if (some #(= e %) '(x y z)) 0 (if (symbol? e) (println 'ILLEGAL) (case (first e) (+) (+ 1 (ops (second e)) (ops (last e))) (if) (+ (ops (second e)) (ops (third e)) (println 'ILLEGAL))) ) ) > (ops '(a)) ILLEGAL > (ops '(if x (+ y z) a)) java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.lang.Number Thrown if ILLEGAL symbol returned > (println 'ILLEGAL) nil > > (ops '(if x nil a)) java.lang.NullPointerException > > (ops '(case x nil a)) cs3180(Prasad) L14Recur