This preview shows pages 1–3. Sign up to view the full content.
This preview has intentionally blurred sections. Sign up to view the full version.
View Full Document
Unformatted text preview: Working with recursion
Readings: HtDP, sections 11, 12, 13 (Intermezzo 2). We can extend the idea of a selfreferential deﬁnition to deﬁning the natural numbers, which leads to the use of recursion in order to write functions that consume numbers. CS 135 Fall 2009 05: Working with recursion 1 Natural numbers
A natural number is either • 0, or • 1 plus a natural number.
The analogy to the selfreferential deﬁnition of lists can be made clearer by deﬁning a “1 plus” function: (deﬁne (add1 n) (+ 1 n)) (add1 0) ⇒ 1 (add1 (add1 0)) ⇒ 2 (add1 (add1 (add1 0))) ⇒ 3
CS 135 Fall 2009 05: Working with recursion 2 We can use the similarity to lists to make a template for natural numbers: • add1 plays the role of cons • 0 plays the role of empty and zero? plays the role of empty? • If we (deﬁne (sub1 n) (− n 1)) then sub1 can play the role of rest • What plays the role of ﬁrst? CS 135 Fall 2009 05: Working with recursion 3 The list template: (deﬁne (mylistfn lst) (cond [(empty? lst) . . . ] [else . . . (ﬁrst lst) . . . (mylistfn (rest lst)) . . . ])) The natural number template: (deﬁne (mynatfn n) (cond [(zero? n) . . . ] [else . . . (mynatfn (sub1 n)) . . . ])) 4 CS 135 Fall 2009 05: Working with recursion Example: a decreasing list
Goal: countdown, which consumes a natural number n and produces a decreasing list of all natural numbers less than or equal to n. (countdown 0) ⇒ (cons 0 empty) (countdown 2) ⇒ (cons 2 (cons 1 (cons 0 empty))) 2 1 0 CS 135 Fall 2009 05: Working with recursion 5 We start ﬁlling in the template: (deﬁne (countdown n) (cond [(zero? n) . . . ] [else . . . (countdown (sub1 n)) . . . ])) If n is 0, we produce the list containing 0, and if n is nonzero, we cons n onto the countdown list for n − 1. CS 135 Fall 2009 05: Working with recursion 6 ;; countdown: nat → (listof nat) ;; example: ;; produces a decreasing list of nats starting at n (checkexpect (countdown 2) (cons 2 (cons 1 (cons 0 empty)))) (deﬁne (countdown n) (cond [(zero? n) (cons 0 empty)] [else (cons n (countdown (sub1 n)))])) CS 135 Fall 2009 05: Working with recursion 7 A condensed trace
(countdown 2) ⇒ (cons 2 (countdown (sub1 2))) ⇒ (cons 2 (countdown 1)) ⇒ (cons 2 (cons 1 (countdown (sub1 1)))) ⇒ (cons 2 (cons 1 (countdown 0))) ⇒ (cons 2 (cons 1 (cons 0 empty))) CS 135 Fall 2009 05: Working with recursion 8 If the function countdown is applied to a negative argument, it will not terminate. The following variation is a little more robust. It can handle negative arguments more gracefully. (deﬁne (countdown n) (cond [(<= n 0) (cons 0 empty)] [else (cons n (countdown (sub1 n)))])) CS 135 Fall 2009 05: Working with recursion 9 From deﬁnition to template
A list is either empty or (cons elt lst), where elt is an element and lst is a list. If mylist is (cons elt lst), then elt is (ﬁrst mylist) and lst is (rest mylist). These expressions go into the template, with recursion on the second one. CS 135 Fall 2009 05: Working with recursion 10 (deﬁne (mylistfn mylist) (cond [(empty? mylist) . . . ] ;; mylistfn: (listof any) → any [else ; mylist is of the form (cons elt lst) . . . (ﬁrst mylist) ; this extracts elt ;; the next line applies recursion to lst . . . (mylistfn (rest mylist)). . . ])) CS 135 Fall 2009 05: Working with recursion 11 The same process applied to the natural number deﬁnition yields the natural number template. A natural number is either 0 or (add1 m), where m is a natural number. If we have a nonzero natural number n, then m is (sub1 n), and the template calls for recursion on this quantity. (deﬁne (mynatfn n) (cond [(zero? n) . . . ] [else . . . (mynatfn (sub1 n)) . . . ])) 12 CS 135 Fall 2009 05: Working with recursion Subintervals of the natural numbers
If we change the base case test from (zero? n) to (= n 7), we can stop the countdown at 7. This corresponds to the following deﬁnition: A natural number greater than or equal to 7 is either • 7, or • one plus a natural number greater than or equal to 7. CS 135 Fall 2009 05: Working with recursion 13 ;; countdownto7: nat → (listof nat) ;; and ending with 7 ;; example: ;; produces a decreasing list of nats starting with n (checkexpect (countdownto7 9) (cons 9 (cons 8 (cons 7 empty)))) (deﬁne (countdownto7 n) (cond [(= n 7) (cons 7 empty)] [else (cons n (countdownto7 (sub1 n)))])) Again, making the base case be (<= n 7) is more robust.
CS 135 Fall 2009 05: Working with recursion 14 We can generalize both countdown and countdownto7 by providing the base value (e.g. 0 or 7) as a parameter b. This corresponding to the following deﬁnition: An integer greater than or equal to b is either: • b, or • 1 plus an integer greater than or equal to b.
The parameter b (for “base”) has to go “along for the ride” in the recursion. CS 135 Fall 2009 05: Working with recursion 15 ;; countdownto: int int → (listof int) ;; and ending with b ;; example: ;; produces a decreasing list of ints starting with n (checkexpect (countdownto 4 2) (cons 4 (cons 3 (cons 2 empty)))) (deﬁne (countdownto n b) (cond [(= n b) (cons b empty)] [else (cons n (countdownto (sub1 n) b))])) CS 135 Fall 2009 05: Working with recursion 16 Another condensed trace
(countdownto 4 2) ⇒ (cons 4 (countdownto (sub1 4) 2)) ⇒ (cons 4 (countdownto 3 2)) ⇒ (cons 4 (cons 3 (countdownto (sub1 3) 2))) ⇒ (cons 4 (cons 3 (countdownto 2 2))) ⇒ (cons 4 (cons 3 (cons 2 empty))) CS 135 Fall 2009 05: Working with recursion 17 countdownto works just ﬁne if we put in negative numbers. (countdownto 1 −2) ⇒ (cons 1 (cons 0 (cons −1 (cons −2 empty))))
Here is the template for counting down to b. (deﬁne (mydowntobfn n b) (cond [(= n b) . . . b . . . ] [else . . . (mydowntobfn (sub1 n) b) . . . ])) CS 135 Fall 2009 05: Working with recursion 18 Some useful notation
The symbol Z is often used to denote the integers. We can add subscripts to deﬁne subsets of the integers. For example, Z≥0 deﬁnes the nonnegative integers, also known as the natural numbers. Z≥b deﬁnes the integers greater than or equal to b. CS 135 Fall 2009 05: Working with recursion 19 Our previous deﬁnition: An integer greater than or equal to b is either: • b, or • 1 plus an integer greater than or equal to b.
Rewritten: An integer in Z≥b is either: • b, or • 1 + k , where k is an integer in Z≥b .
CS 135 Fall 2009 05: Working with recursion 20 Going the other way
What if we want an increasing count? Consider the nonpositive integers Z≤0 . An integer in Z≤0 is either: • 0, or • k − 1, where k is an integer in Z≤0 .
Examples: If an integer i is of the form k −1 is (0 − 1), −2 is ((−1) − 1). (add1 i). This suggests the following template.
CS 135 Fall 2009 05: Working with recursion − 1, then k is equal to
21 Notice the notation for subsets of the integers in contracts. ;; mynonposfn: int[≤ 0] → any (deﬁne (mynonposfn n) (cond [(zero? n) . . . ] [else . . . (mynonposfn (add1 n)) . . . ])) We can use this to develop a function to produce lists such as (cons −2 (cons −1 (cons 0 empty))). CS 135 Fall 2009 05: Working with recursion 22 ;; countup: int[≤ 0] → (listof int[≤ 0]) ;; counts up to zero ;; example: (checkexpect (countup −2) (cons −2 (cons −1 (cons 0 empty)))) (deﬁne (countup n) (cond [(zero? n) (cons 0 empty)] [else (cons n (countup (add1 n)))])) CS 135 Fall 2009 05: Working with recursion 23 As before, we can generalize this to counting up to b. An integer in Z≤b is either: • b, or • k − 1, where k is an integer in Z≤b .
For b = 14, 14 is an integer in Z≤14 . 13 is an integer in Z≤14 because it is 14 − 1. 12 is because it is 13 − 1, and so on.
In other words, 12 is (sub1 (sub1 14)). CS 135 Fall 2009 05: Working with recursion 24 We go through the same process as before to derive the template. An integer n in Z≤b which is not b must be of the form k in Z≤b . To get k from n, we must add one to n. (deﬁne (myuptobfn n b) (cond [(= n b) . . . b . . . ] [else . . . (myuptobfn (add1 n) b) . . . ])) − 1 for k CS 135 Fall 2009 05: Working with recursion 25 ;; countupto: int int → (listof int) ;; and ending with b ;; example: ;; produces an increasing list of ints starting with n (checkexpect (countupto 6 8) (cons 6 (cons 7 (cons 8 empty)))) (deﬁne (countupto n b) (cond [(= n b) (cons b empty)] [else (cons n (countupto (add1 n) b))])) CS 135 Fall 2009 05: Working with recursion 26 Yet another condensed trace
(countupto 6 8) ⇒ (cons 6 (countupto (add1 6) 8)) ⇒ (cons 6 (countupto 7 8)) ⇒ (cons 6 (cons 7 (countupto (add1 7) 8))) ⇒ (cons 6 (cons 7 (countupto 8 8))) ⇒ (cons 6 (cons 7 (cons 8 empty))) CS 135 Fall 2009 05: Working with recursion 27 Many imperative programming languages offer several language constructs to do repetition: for i = 1 to 10 do { . . . } Scheme offers one construct – recursion – that is ﬂexible enough to handle these situations and more. We will soon see how to use Scheme’s abstraction capabilities to handle many common uses of recursion. CS 135 Fall 2009 05: Working with recursion 28 When you are learning to use recursion, sometimes you will “get it backwards” and use the countdown pattern when you should be using the countup pattern, or viceversa. Avoid using the builtin list function reverse to ﬁx your error. It cannot always save a computation done in the wrong order. Instead, learn to ﬁx your mistake by using the right pattern. You may not use reverse in your functions until we say
otherwise (much later in the course). CS 135 Fall 2009 05: Working with recursion 29 More complicated situations
As before, we may need to introduce auxiliary functions during the composition of a function. These may or may not be recursive themselves. Sorting a list of numbers provides a good example; in this case the solution follows easily from the templates and design process. In this course and CS 136, we will see several different sorting algorithms. CS 135 Fall 2009 05: Working with recursion 30 Filling in the list template
;; sort: (listof num) → (listof num) (deﬁne (sort alon) (cond [(empty? alon) . . . ] [ else . . . (ﬁrst alon) . . . (sort (rest alon)) . . . ])) If the list alon is empty, so is the result. Otherwise, the template suggests doing something with the ﬁrst element of the list, and the sorted version of the rest.
CS 135 Fall 2009 05: Working with recursion ;; produces a list sorted in nondecreasing order 31 (deﬁne (sort alon) (cond [(empty? alon) empty] [ else (insert (ﬁrst alon) (sort (rest alon)))])) insert is a recursive auxiliary function which consumes a number and a sorted list, and adds the number to the sorted list. CS 135 Fall 2009 05: Working with recursion 32 A condensed trace of sort
(sort (cons 2 (cons 4 (cons 3 empty)))) ⇒ (insert 2 (sort (cons 4 (cons 3 empty)))) ⇒ (insert 2 (insert 4 (sort (cons 3 empty)))) ⇒ (insert 2 (insert 4 (insert 3 empty))) ⇒ (insert 2 (insert 4 (cons 3 empty))) ⇒ (insert 2 (cons 3 (cons 4 empty))) ⇒ (cons 2 (cons 3 (cons 4 empty))) CS 135 Fall 2009 05: Working with recursion 33 The auxiliary function insert
We again use the list template for insert. ;; insert: num (listof num) → (listof num) ;; the resulting list is also sorted. (deﬁne (insert n alon) (cond [(empty? alon) . . . ] [ else . . . (ﬁrst alon) . . . (insert n (rest alon)) . . . ])) ;; adds the number n to the sorted list alon so that CS 135 Fall 2009 05: Working with recursion 34 If alon is empty, the result is the list containing just n. If alon is not empty, another conditional expression is needed. n is the ﬁrst number in the result if it is less than or equal to the ﬁrst number in alon. Otherwise, the ﬁrst number in the result is the ﬁrst number in alon, and the rest of the result is what we get when we insert n into (rest alon). CS 135 Fall 2009 05: Working with recursion 35 (deﬁne (insert n alon) (cond [(empty? alon) (cons n empty)] [ else (cond [(<= n (ﬁrst alon)) (cons n alon)] [ else (cons (ﬁrst alon) (insert n (rest alon)))])])) CS 135 Fall 2009 05: Working with recursion 36 Tracing insert
(insert 4 (cons 1 (cons 2 (cons 5 empty)))) ⇒ (cons 1 (insert 4 (cons 2 (cons 5 empty)))) ⇒ (cons 1 (cons 2 (insert 4 (cons 5 empty)))) ⇒ (cons 1 (cons 2 (cons 4 (cons 5 empty))))
This is known as insertion sort. CS 135 Fall 2009 05: Working with recursion 37 List abbreviations
Now that we understand lists, we can abbreviate them. The expression (cons exp1 (cons exp2 (. . . (cons expn empty). . . ))) can be abbreviated as (list exp1 exp2 . . . expn) The result of the trace we did on the last slide can be expressed as (list 1 2 4 5).
CS 135 Fall 2009 05: Working with recursion 38 Beginning Student With List Abbreviations also provides some shortcuts for accessing speciﬁc elements of lists. (second mylist) is an abbreviation for (ﬁrst (rest mylist)). third, fourth, and so on up to eighth are also deﬁned. Use these sparingly to improve readability, and use list only to construct long lists. The templates we have developed remain very useful, and cons continues to be the main list constructor function used in code. CS 135 Fall 2009 05: Working with recursion 39 Quoting lists
If the expressions consist of just symbols and values (as in this case) the list abbreviation can be further abbreviated using the quote notation we used for symbols. (cons ’red (cons ’blue (cons ’green empty))) can be written ’(red blue green). (list 5 4 3 2) can be written ’(5 4 3 2), because quoted numbers evaluate to numbers; that is, ’1 is the same as 1. What is ’() ? CS 135 Fall 2009 05: Working with recursion 40 Lists containing lists
Lists can contain anything, including other lists, at which point these abbreviations can improve readability. Here are two different twoelement lists. 1 3 2 4 (cons 1 (cons 2 empty)) (cons 3 (cons 4 empty)) 41 CS 135 Fall 2009 05: Working with recursion Here is a oneelement list whose single element is one of the twoelement lists we saw above. 3 4 (cons (cons 3 (cons 4 empty)) empty) We can create a twoelement list by consing the other list onto this oneelement list.
CS 135 Fall 2009 05: Working with recursion 42 We can create a twoelement list, each of whose elements is itself a twoelement list. 3 1 2 4 (cons (cons 1 (cons 2 empty)) (cons (cons 3 (cons 4 empty)) empty)) CS 135 Fall 2009 05: Working with recursion 43 We have several ways of expressing this list in Scheme: (cons (cons 1 (cons 2 empty)) (cons (cons 3 (cons 4 empty)) empty)) (list (list 1 2) (list 3 4)) ’((1 2) (3 4)) Clearly, the abbreviations are more expressive. CS 135 Fall 2009 05: Working with recursion 44 Lists versus structures
Since lists can contain lists, they offer an alternative to using structures. For example, we deﬁned a payroll as a list of salary records, each one being a twoﬁeld structure. We could have instead deﬁned an altpayroll as a list of twoelement lists. (list (list ’JaneDoe 50000) (list ’DaKou 15500) (list ’MuzaAlKhwarismi 100000)) 45 CS 135 Fall 2009 05: Working with recursion We can use a data deﬁnition to be precise about lists containing twoelement lists. An altpayroll is either: • empty, or • (cons (list name salary) aprl), where
– name is a string, – salary is a number, and – aprl is an altpayroll. This leads to a template for altpayrolls.
CS 135 Fall 2009 05: Working with recursion 46 (deﬁne (myapyrlfn apyrl) (cond [(empty? apyrl) . . . ] [else . . . (ﬁrst (ﬁrst apyrl)) . . . ; name of ﬁrst . . . (second (ﬁrst apyrl)) . . . ; salary of ﬁrst . . . (myapyrlfn (rest apyrl)). . . ])) Example: a function namelist which consumes an altpayroll and produces the corresponding list of names. CS 135 Fall 2009 05: Working with recursion 47 (deﬁne (namelist apyrl) (cond [(empty? apyrl) empty] [else (cons (ﬁrst (ﬁrst apyrl)) ; name of ﬁrst (namelist (rest apyrl)))])) This code is less readable, because it uses only lists, instead of structures, and so is more genericlooking. We can ﬁx this with a few deﬁnitions. CS 135 Fall 2009 05: Working with recursion 48 (deﬁne (name x) (ﬁrst x)) (deﬁne (salary x) (second x)) (deﬁne (namelist apyrl) (cond [(empty? apyrl) empty] [else (cons (name (ﬁrst apyrl)) (namelist (rest apyrl)))])) This is one of the ways that structures can be simulated in standard Scheme. There are others, as we will see.
CS 135 Fall 2009 05: Working with recursion 49 Dictionaries
You know dictionaries as books in which you look up a word and get a deﬁnition or a translation. More generally, a dictionary contains a number of keys, each with an associated value. Our task is to store the set of (key,value) pairs to support the operations lookup, add, and remove. CS 135 Fall 2009 05: Working with recursion 50 Association lists
One simple solution uses an association list, which is just a list of (key, value) pairs. An association list (al) is either: • empty, or • (cons (list k v) alst), where
– k is a number (the key), – v is a string (the value), and – alst is an association list.
CS 135 Fall 2009 05: Working with recursion 51 ;; lookupal: num al → (union string false) (deﬁne (lookupal k alst) (cond [(empty? alst) false] ;; produces the value associated with k, or false if k not present [(equal? k (ﬁrst (ﬁrst alst))) (second (ﬁrst alst))] [else (lookupal k (rest alst))])) The primitive function assq does something similar (it returns the pair). CS 135 Fall 2009 05: Working with recursion 52 We will leave the add and remove functions as exercises. This solution is simple enough that it is often used for small dictionaries. For a large dictionary, association lists are inefﬁcient in the case where the key is not present and the whole list must be searched. Keeping the list in sorted order might improve some searches, but there is still a case where the whole list is searched. In module 07, we will see how to avoid this. CS 135 Fall 2009 05: Working with recursion 53 Why use lists containing lists?
If we deﬁne an alttaxroll as a list of twoelement lists, each sublist holding name and tax owed, we could reuse the namelist function to produce a list of names from an alttaxroll. Our original structurebased payroll and taxroll deﬁnitions require two different (but very similar) functions. We will exploit this ability to reuse code written to use “generic” lists when we discuss abstract list functions later in the course. CS 135 Fall 2009 05: Working with recursion 54 Why use structures?
Structure is often present in a computational task, or can be deﬁned to help handle a complex situation. Using structures helps avoid some programming errors (e.g., accidentally extracting a list of salaries instead of names). Our design recipes can be adapted to give guidance in writing functions using complicated structures. Most mainstream programming languages provide structures for use in larger programming tasks. CS 135 Fall 2009 05: Working with recursion 55 Some programming languages which are strongly typed do provide mechanisms that avoid having to duplicate code for types that are structurally identical or similar. Inheritance in objectoriented languages is one such mechanism. Such features could be easily built into the teaching languages, but are not, because the goal is to make a transition to other languages. The full version of PLT Scheme (which goes beyond the standard, as we will see in CS 136) does have such features. CS 135 Fall 2009 05: Working with recursion 56 Different kinds of lists
When we introduced lists in module 04, the items they contained were not lists. These were ﬂat lists. We have just seen lists of lists in our example of lists containing twoelement ﬂat lists. In module 10 we will use lists containing unbounded ﬂat lists. In module 07, we will see nested lists, in which lists may contain lists that contain lists, and so on to an arbitrary depth. CS 135 Fall 2009 05: Working with recursion 57 Goals of this module
You should understand the recursive deﬁnition of a natural number, and how it leads to a template for recursive functions that consume natural numbers. You should understand how subsets of the integers greater than or equal to some bound m, or less than or equal to such a bound, can be deﬁned recursively, and how this leads to a template for recursive functions that “count down” or “count up”. You should be able to write such functions. CS 135 Fall 2009 05: Working with recursion 58 You should understand the principle of insertion sort, and how the functions involved can be created using the design recipe. You should be able to use list abbreviations and quote notation for lists where appropriate. You should be able to construct and work with lists that contain lists. You should understand the similar uses of structures and ﬁxedsize lists, and be able to write functions that consume either type of data. CS 135 Fall 2009 05: Working with recursion 59 ...
View
Full
Document
This note was uploaded on 02/12/2010 for the course CS 135 taught by Professor Vasiga during the Fall '07 term at Waterloo.
 Fall '07
 VASIGA
 Recursion

Click to edit the document details