Comp210 Lecture # 8    Fall 2002

Recursion

The dream in a dream in a dream in a dream in a......

As we talked about before, lists are self-referential data structures, that is, they a structures that contain one of their own type. For instance, we could define a list of numbers as such:

;; A list-of-numbers "lon" is either
;; - empty
;; - cons, which has a first, which is a number,
;; and a rest, which is a list.
;; (cons num lon)

Since the definition of a lon includes a lon, we call this a "recursive definition".

Question: Isn't this a circular argument? How can we define anything in terms of itself? Is this definition really getting anywhere?

 

 

The answer is YES, it is a circular argument.....

 

Except that there are two parts to the definition (Gotcha!).

A recursive definition consists of at least 2 parts:

1) a base case that does not contain a reference to its own type.

2) an inductive case that does contain a reference to its own type.

The base case defines the terminating situation for the structure or process.

The inductive case defines how one can traverse the structure.

 

What does the recursive definition of a list imply about the template?

Nothing out of the ordinary actually:

(define (f-lon ...a-lon...)

(cond

[(empty? a-lon)...]    ;; BASE CASE
[(cons? a-lon) ... (first a-lon)...(rest a-lon)]))     ;; INDUCTIVE CASE

ALL RECURSIVE ALGORITHMS CAN BE DECOMPOSED INTO A BASE CASE AND AN INDUCTIVE CASE.

 

Variant: The specific behaviors of the base and inductive cases.

Invariant: The code used to glue the base and inductive cases together into a working function = template code.

 

Guidelines for writing recursive algorithms:

  1. Focus on writing the base case and inductive cases separately! They have nothing to do with each other.

  2. Do not think about how the base and inductive cases are connected to the rest of the function -- that is the job of the invariant template code. This code does not even appear in some languages, e.g. declarative languages such as Haskell and ML as well as in well-written object-oriented programs.

  3. Remember that a list consists of first and rest only, so deal with just those two elements. DO NOT VIOLATE THE ENCAPSULATION OF REST!!

Examples:

The cases are color-coded: base case & inductive case

Summing a list of numbers:

;; sum: lon -> num
;; Sums the numbers in a lon.

(define (sum a-lon)

(cond

[(empty? a-lon) 0]
[(cons? a-lon) (+ (first a-lon) (sum (rest a-lon)))]))

"sum tests:"
(= 10 (sum l1))
(= 0 (sum empty))
(= 2 (sum l2))

Advice: You should know how to hand-evaluate a recursive function...hint, hint, nudge, nudge!

Find a value in a list of numbers

;; find_val: lon num -> boolean
;; Returns true if x is in a-lon
;; otherwise returns false

(define (find_val a-lon x)

(cond

[(empty? a-lon) false]
[(cons? a-lon)

(cond

[ (= x (first a-lon)) true]                   ;; This is a "terminal case"
[else (find_val (rest a-lon) x)])
]))

" find_val tests:"
(boolean=? false (find_val empty 42))
(boolean=? true (find_val l1 3))
(boolean=? false
(find_val l2 3))

Class Exercise: Write a function to calculate the sum-of-squares of a lon.

Creating a new list: Copying a list

;;copy: lon -> lon
;; copies a lon

(define (copy a-lon)

(cond

[(empty? a-lon) empty]
[(cons? a-lon) (cons (first a-lon) (copy (rest a-lon)))]))

"copy tests:"
(equal? empty (copy empty))
(equal? l1 (copy l1))
(equal? l2 (copy l2))

"Note, the following results in false:"

(eq? l1 (copy l1))

equals? compares the value of every element, including the values contained in any composed structures.

eq? simply checks if they are identically the same entity, i.e. the same piece of memory allocation.

 

Class Exercise: Write a function to add a number to every element in a lon, returning the new lon.

The code for today, including the class exercises can be downloaded here.

Note: (list 1 2 3) is the same as (cons 1 (cons 2 (cons 3 empty)))

Warning!

(list 5 (list 3)) is not the same as (cons 5 (cons 3 empty)) !!!

(list 5 (list 3) is the same as (cons 5 (cons (cons 3 empty) empty)) -- the second element is a list, not a number!

 

 

©2002 Stephen Wong