CSC151.02, Class 15: Repetition and Recursion
Admin
* Sorry about the delay posting homework 2.
* Writeup 1 is now graded.
* No reading.
* Exam 1 distributed. Due a week from tomorrow.
Overview:
* Repetition
* Recursion
* Examples / Scheme
* Lab
How Sam Grades:
* Oddly: Don't be bothered by low homework grades. Do be bothered by low exam grades.
* On the computer: I will generally email your homework back to you with interspersed comments.
Key Things from Writeup 1
* Postconditions are vague "The result is a number"
* One of your goals when you write postconditions should be to specify the result as carefully as possible.
* E.g., quadratic-root:
root is a "zero of the function". That is, a*root*root + b*root + c = 0
* E.g., swap first two
Input list: lst
Output list: newlst
Postconditions:
(1) (list-ref newlst 0) = (list-ref lst 1)
(2) (list-ref newlst 1) = (list-ref lst 0)
(3) (list-ref newlst i) = (list-ref lst i) for all other reasonable i
----------------------------------------
Repetition
Some standard components to algorithms:
* Naming values (define)
* Building subroutines/procedures (define ... lambda)
* Naming input to a procedure (see above)
* Sequence operations:
* We know how to sequence conditionals
* We also know how Scheme usually sequences: It evaluates the arguments to a procedure before it applies the procedure
(mix
(mix (sift flour)
(measure sugar))
(mix (measure milk)
(melt butter)
(dump-lots-of vanilla)))
* Conditionals: How to choose between things. (if ...) (cond ...)
* Repetition
* Scheme uses a strange (but nicely mathematical) technique for repetition: recursion
* To solve a big problem,
(1) remove part of the problem
(2) solve the smaller problem
(3) reuse the solution to the small problem in the solution to the big problem
How is this helpful? Consider the problem of finding the length of a list.
Suppose we need to write length.
(define length
(lambda (lst)
(if (null? lst) 0
(+ 1 (length (cdr lst))))))
(length (list 1 2 3 4 5))
=> (+ 1 (length (cdr (list 1 2 3 4 5))))
=> (+ 1 (length (list 2 3 4 5)))
=> (+ 1 (+ 1 (length (list 3 4 5))))
=> (+ 1 (+ 1 (+ 1 (length (list 4 5)))))
=> (+ 1 (+ 1 (+ 1 (+ 1 (length (list 5))))))
=> (+ 1 (+ 1 (+ 1 (+ 1 (+ 1 (length (list)))))))
=> (+ 1 (+ 1 (+ 1 (+ 1 (+ 1 0))))))
=> (+ 1 (+ 1 (+ 1 (+ 1 1))))
=> (+ 1 (+ 1 (+ 1 2)))
=> (+ 1 (+ 1 3))
=> (+ 1 4)
=> 5
Runaway recursion
(define length
(lambda (lst)
(if (null? lst)
0
(+ 1 (length lst)))))
(length (list 1 2 3 4 5))
=> (+ 1 (length (list 1 2 3 4 5)))
=> (+ 1 (+ 1 (length (list 1 2 3 4 5))))
=> (+ 1 (+ 1 (+ 1 (length (list 1 2 3 4 5)))))
How to avoid "runaway" recursion: Always make sure that your argument is "smaller" in the recursive call to the same procedure.
factorial
n! = 1 * 2 * .... * n-1 * n
E.g.
5! = 1 * 2 * 3 * 4 * 5
Most mathematicians say
0! is 1
n! is n*(n-1)!, for positive n
In Scheme
(define factorial
(lambda (n)
(if (zero? n)
1
(* n (factorial (- n 1))))))
Note that if you start this program with a negative n, it will run forever.
Standard form of a recursive procedure in Scheme
(define NAME
(lambda (PARAMS)
(if (SIMPLE PARAMS)
SIMPLE-RESULT
(EXPAND (NAME (SIMPLIFY PARAMS))))))
For factorial
NAME: factorial
PARAMS: n
SIMPLE: zero?
SIMPLE-RESULT 1
SIMPLIFY: subtract 1
EXPAND: multiply by n
NAME: length
PARAMS: lst
SIMPLE: null?
SIMPLE-RESULT: 0
SIMPLIFY: cdr
EXPAND: add 1