plt /8/ Inductive Sets of Data Programming Language Essentials 2nd edition Chapter 1.2 Recursively Specified Programs
plt /8/ Recursion recursion can lead to divide-and-conquer: solution for a small problem 'by hand'. solution for a bigger problem by recursively invoking the solution for smaller problems. Examples: Factorial. Greatest common divisor (Euclid). Tower of Hanoi. Quicksort.
plt /8/ Recursively Specified Programs patterned after proof by induction. to compute x n : e(0, x) = 1, because x 0 is 1. e(n, x) = x * e(n-1, x), because x n is x * x n-1. induction proves that e(n,x) is x n : (0) Hypothesis: e(k, x) is x k. (1)true for k=0, see above. (2)assume true up to k. Consider e(k+1, x). This is x * e(k, x) by definition, x * x k by assumption, and therefore x k+1.
plt /8/ Recursively Specified Programs (2) to compute x n : e(0, x) = 1, because x 0 is 1. e(n, x) = x * e(n-1, x), because x n is x * x n-1. (define e (lambda (n x) (if (zero? n) 1 (* x (e (- n 1) x) ) )
plt /8/ count-nodes bintree: 'Number' | '(' 'Symbol' bintree bintree ')' (define count-nodes (lambda (bintree) (if (number? bintree) 1 (+ (count-nodes (cadr bintree)) (count-nodes (caddr bintree)) 1 ) )
plt /8/ Deriving Programs from BNF l-of-nums: '()' l-of-nums: '(' 'Number' '.' l-of-nums ')' pattern program after data (structural induction) one procedure for one nonterminal (define list-of-numbers? (lambda (x) (if (null? x) #t (and (pair? x) (number? (car x)) (list-of-numbers? (cdr x)) ) )
plt /8/ Proof by Induction (0) Hypothesis: l-of-n? works on lists of length k. (1)k=0: empty list, l-of-n? returns true. ok. (2)assume true up to k. Consider k+1. By grammar, car must be a number and cdr a list of numbers. cdr of list of k+1 elements has k elements, i.e., l-of-n? can be applied. ok. proof does not discover that pair? is necessary because proof assumes list arguments
plt /8/ nth-elt like list-ref, returns 0..th element of list. (define nth-elt (lambda (list n) (if (null? list) (error 'nth-elt "List too short." (+ n 1) "elements") (if (zero? n) (car list) (nth-elt (cdr list) (- n 1)) ) )
plt /8/ error lists Scheme Request for Implementation $ scheme48 >,open srfi-23 Newly accessible in user: (error) > (define nth-elt … > (nth-elt '(1 2 3) -4) Error: nth-elt "List too short." -6 "elements"
plt /8/ list-length like length, returns number of elements in list (define list-length (lambda (list) (if (null? list) 0 (+ 1 (list-length (cdr list))) ) ) )
plt /8/ fragile vs. robust fragile procedures do not check the types of their arguments. (define list-length ; robust version (lambda (list) (if (list? list) (if (null? list) 0 (+ 1 (list-length (cdr list))) ) (error 'list-length "Not a list:" list) ) ) )
plt /8/ remove-first returns list without first occurrence of symbol list: '()' | '(' symbol '.' list ')' (define remove-first ; fragile (lambda (symbol list) (if (null? list) list … ) ) )
plt /8/ remove-first returns list without first occurrence of symbol list: '()' | '(' symbol '.' list ')' (define remove-first ; fragile (lambda (symbol list) (if (null? list) list (if (eqv? (car list) symbol) (cdr list) … ) )
plt /8/ remove-first returns list without first occurrence of symbol list: '()' | '(' symbol '.' list ')' (define remove-first ; fragile (lambda (symbol list) (if (null? list) list (if (eqv? (car list) symbol) (cdr list) (cons (car list) (remove-first symbol (cdr list)) ) ) ) ) )
plt /8/ remove returns list without all occurrences of symbol list: '()' | '(' symbol '.' list ')' (define remove ; fragile (lambda (symbol list) (if (null? list) list (if (eqv? (car list) symbol) (remove symbol (cdr list)) (cons (car list) (remove symbol (cdr list)) ) ) ) ) )
plt /8/ subst returns s-list with any old replaced by new > (subst 'a 'b '((b c) (b () d))) '((a c) (a () d)) s-list: '(' symbol-expression* ')' symbol-expression: 'Symbol' | s-list s-list: '()' | '(' symbol-expression '.' s-list ')' symbol-expression: 'Symbol' | s-list pair avoids need for another rule
plt /8/ subst s-list: '()' | '(' symbol-expression '.' s-list ')' (define subst ; fragile (lambda (new old slist) (if (null? slist) slist (cons (subst-se new old (car slist)) (subst new old (cdr slist)) ) )
plt /8/ subst-se symbol-expression: 'Symbol' | s-list (define subst-se (lambda (new old se) (if (symbol? se) (if (eqv? se old) new se) (subst new old se) ) ) ) mutual recursion divide-and-conquer because of grammar
plt /8/ subst (define subst ; fragile (lambda (new old slist) (define symbol-expression ; local procedure (lambda (se) ; shares parameters (if (symbol? se) (if (eqv? se old) new se) (subst new old se) ) ) ) (if (null? slist) slist (cons (symbol-expression (car slist)) (subst new old (cdr slist)) ) )
plt /8/ notate-depth returns s-list with symbols annotated for depth > (notate-depth '((b c) (b () d))) '(((b 1) (c 1)) ((b 1) () (d 1))) notate-depth: s-list s-list: '()' | '(' symbol-expression '.' s-list ')' symbol-expression: 'Symbol' | s-list notate-depth needed to mark start at level zero
plt /8/ notate-depth notate-depth: s-list (define notate-depth ; fragile (lambda (slist) (s-list slist 0) )
plt /8/ notate-depth s-list: '()' | '(' symbol-expression '.' s-list ')' (define notate-depth (lambda (slist) (define s-list (lambda (slist d) (if (null? slist) slist (cons (symbol-expression (car slist) d) (s-list (cdr slist) d) ) ) ) ) (s-list slist 0) )
plt /8/ notate-depth symbol-expression: 'Symbol' | s-list (define notate-depth (lambda (slist) (define s-list … (define symbol-expression (lambda (se d) (if (symbol? se) (list se d) (s-list se (+ d 1)) ) ) ) (s-list slist 0) )
plt /8/ notate-depth (define notate-depth (lambda (slist) (define s-list (lambda (slist d) (if (null? slist) slist (cons (symbol-expression (car slist) d) (s-list (cdr slist) d) ) ) ) ) (define symbol-expression (lambda (se d) (if (symbol? se) (list se d) (s-list se (+ d 1)) ) ) ) (s-list slist 0) )
plt /8/ Other Patterns of Recursion l-of-nums: '()' l-of-nums: '(' 'Number' '.' l-of-nums ')' compute sum by structural induction: (define list-sum (lambda (x) (if (null? x) 0 (+ (car x)) (list-sum (cdr x)) ) )
plt /8/ Other Patterns of Recursion (2) vector is not suitable for structural induction, so: (define vector-sum (lambda (v) (partial-vector-sum v (vector-length v)) ) (define partial-vector-sum (lambda (v n) (if (zero? n) 0 (+ (vector-ref v (- n 1)) ; last (partial-vector-sum v (- n 1)) ) )
plt /8/ Other Patterns of Recursion (3) (define vector-sum (lambda (v) (letrec ((partial-vector-sum (lambda (n) (if (zero? n) 0 (+ (vector-ref v (- n 1)) ; last (partial-vector-sum (- n 1)) ))) ) ) (partial-vector-sum (vector-length v)) ) ) ) proof by induction on vector length