270 likes | 396 Views
Inductive Sets of Data. Programming Language Essentials 2nd edition Chapter 1.2 Recursively Specified Programs. Recursion. recursion can lead to divide-and-conquer: solution for a small problem 'by hand'.
E N D
Inductive Sets of Data • Programming Language Essentials • 2nd edition • Chapter 1.2 Recursively Specified Programs
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.
Recursively Specified Programs • patterned after proof by induction. • to compute xn: • e(0, x) = 1, because x0 is 1. • e(n, x) = x * e(n-1, x), because xn is x * xn-1. • induction proves that e(n,x) is xn: • (0) Hypothesis: e(k, x) is xk. • (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 * xk by assumption, and therefore xk+1.
Recursively Specified Programs (2) • to compute xn: • e(0, x) = 1, because x0 is 1. • e(n, x) = x * e(n-1, x), because xn is x * xn-1. • (define e • (lambda (n x) • (if (zero? n) • 1 • (* x • (e (- n 1) x) • ) ) ) )
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 • ) ) ) )
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)) • ) ) ) )
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
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)) • ) ) ) )
error • http://srfi.schemers.org/ • 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"
list-length • like length, returns number of elements in list • (define list-length • (lambda (list) • (if (null? list) • 0 • (+ 1 (list-length (cdr list))) • ) ) )
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) • ) ) )
remove-first • returns list without first occurrence of symbol • list: '()' | '(' symbol '.' list ')' • (define remove-first ; fragile • (lambda (symbol list) • (if (null? list) • list • … • ) ) )
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) • … • ) ) ) )
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)) • ) ) ) ) )
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)) • ) ) ) ) )
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
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)) • ) ) ) )
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
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)) • ) ) ) )
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
notate-depth • notate-depth: s-list • (define notate-depth ; fragile • (lambda (slist) • (s-list slist 0) • ) )
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) • ) )
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) • ) )
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) • ) )
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)) • ) ) ) )
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)) • ) ) ) )
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