05-recursion-post3up

05-recursion-post3up - Working with recursion Readings:...

Info iconThis preview shows pages 1–3. Sign up to view the full content.

View Full Document Right Arrow Icon

Info iconThis preview has intentionally blurred sections. Sign up to view the full version.

View Full DocumentRight Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: Working with recursion Readings: HtDP, sections 11, 12, 13 (Intermezzo 2). We can extend the idea of a self-referential definition to defining 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 self-referential definition of lists can be made clearer by defining a “1 plus” function: (define (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 (define (sub1 n) (− n 1)) then sub1 can play the role of rest • What plays the role of first? CS 135 Fall 2009 05: Working with recursion 3 The list template: (define (my-list-fn lst) (cond [(empty? lst) . . . ] [else . . . (first lst) . . . (my-list-fn (rest lst)) . . . ])) The natural number template: (define (my-nat-fn n) (cond [(zero? n) . . . ] [else . . . (my-nat-fn (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 filling in the template: (define (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 (check-expect (countdown 2) (cons 2 (cons 1 (cons 0 empty)))) (define (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. (define (countdown n) (cond [(<= n 0) (cons 0 empty)] [else (cons n (countdown (sub1 n)))])) CS 135 Fall 2009 05: Working with recursion 9 From definition to template A list is either empty or (cons elt lst), where elt is an element and lst is a list. If my-list is (cons elt lst), then elt is (first my-list) and lst is (rest my-list). These expressions go into the template, with recursion on the second one. CS 135 Fall 2009 05: Working with recursion 10 (define (my-list-fn my-list) (cond [(empty? my-list) . . . ] ;; my-list-fn: (listof any) → any [else ; my-list is of the form (cons elt lst) . . . (first my-list) ; this extracts elt ;; the next line applies recursion to lst . . . (my-list-fn (rest my-list)). . . ])) CS 135 Fall 2009 05: Working with recursion 11 The same process applied to the natural number definition 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. (define (my-nat-fn n) (cond [(zero? n) . . . ] [else . . . (my-nat-fn (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 definition: 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 ;; countdown-to-7: nat → (listof nat) ;; and ending with 7 ;; example: ;; produces a decreasing list of nats starting with n (check-expect (countdown-to-7 9) (cons 9 (cons 8 (cons 7 empty)))) (define (countdown-to-7 n) (cond [(= n 7) (cons 7 empty)] [else (cons n (countdown-to-7 (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 countdown-to-7 by providing the base value (e.g. 0 or 7) as a parameter b. This corresponding to the following definition: 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 ;; countdown-to: int int → (listof int) ;; and ending with b ;; example: ;; produces a decreasing list of ints starting with n (check-expect (countdown-to 4 2) (cons 4 (cons 3 (cons 2 empty)))) (define (countdown-to n b) (cond [(= n b) (cons b empty)] [else (cons n (countdown-to (sub1 n) b))])) CS 135 Fall 2009 05: Working with recursion 16 Another condensed trace (countdown-to 4 2) ⇒ (cons 4 (countdown-to (sub1 4) 2)) ⇒ (cons 4 (countdown-to 3 2)) ⇒ (cons 4 (cons 3 (countdown-to (sub1 3) 2))) ⇒ (cons 4 (cons 3 (countdown-to 2 2))) ⇒ (cons 4 (cons 3 (cons 2 empty))) CS 135 Fall 2009 05: Working with recursion 17 countdown-to works just fine if we put in negative numbers. (countdown-to 1 −2) ⇒ (cons 1 (cons 0 (cons −1 (cons −2 empty)))) Here is the template for counting down to b. (define (my-downto-b-fn n b) (cond [(= n b) . . . b . . . ] [else . . . (my-downto-b-fn (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 define subsets of the integers. For example, Z≥0 defines the non-negative integers, also known as the natural numbers. Z≥b defines the integers greater than or equal to b. CS 135 Fall 2009 05: Working with recursion 19 Our previous definition: 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 non-positive 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. ;; my-nonpos-fn: int[≤ 0] → any (define (my-nonpos-fn n) (cond [(zero? n) . . . ] [else . . . (my-nonpos-fn (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: (check-expect (countup −2) (cons −2 (cons −1 (cons 0 empty)))) (define (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. (define (my-upto-b-fn n b) (cond [(= n b) . . . b . . . ] [else . . . (my-upto-b-fn (add1 n) b) . . . ])) − 1 for k CS 135 Fall 2009 05: Working with recursion 25 ;; countup-to: int int → (listof int) ;; and ending with b ;; example: ;; produces an increasing list of ints starting with n (check-expect (countup-to 6 8) (cons 6 (cons 7 (cons 8 empty)))) (define (countup-to n b) (cond [(= n b) (cons b empty)] [else (cons n (countup-to (add1 n) b))])) CS 135 Fall 2009 05: Working with recursion 26 Yet another condensed trace (countup-to 6 8) ⇒ (cons 6 (countup-to (add1 6) 8)) ⇒ (cons 6 (countup-to 7 8)) ⇒ (cons 6 (cons 7 (countup-to (add1 7) 8))) ⇒ (cons 6 (cons 7 (countup-to 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 flexible 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 vice-versa. Avoid using the built-in list function reverse to fix your error. It cannot always save a computation done in the wrong order. Instead, learn to fix 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) (define (sort alon) (cond [(empty? alon) . . . ] [ else . . . (first alon) . . . (sort (rest alon)) . . . ])) If the list alon is empty, so is the result. Otherwise, the template suggests doing something with the first 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 (define (sort alon) (cond [(empty? alon) empty] [ else (insert (first 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. (define (insert n alon) (cond [(empty? alon) . . . ] [ else . . . (first 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 first number in the result if it is less than or equal to the first number in alon. Otherwise, the first number in the result is the first 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 (define (insert n alon) (cond [(empty? alon) (cons n empty)] [ else (cond [(<= n (first alon)) (cons n alon)] [ else (cons (first 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 specific elements of lists. (second my-list) is an abbreviation for (first (rest my-list)). third, fourth, and so on up to eighth are also defined. 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 two-element 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 one-element list whose single element is one of the two-element lists we saw above. 3 4 (cons (cons 3 (cons 4 empty)) empty) We can create a two-element list by consing the other list onto this one-element list. CS 135 Fall 2009 05: Working with recursion 42 We can create a two-element list, each of whose elements is itself a two-element 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 defined a payroll as a list of salary records, each one being a two-field structure. We could have instead defined an alt-payroll as a list of two-element 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 definition to be precise about lists containing two-element lists. An alt-payroll is either: • empty, or • (cons (list name salary) aprl), where – name is a string, – salary is a number, and – aprl is an alt-payroll. This leads to a template for alt-payrolls. CS 135 Fall 2009 05: Working with recursion 46 (define (my-apyrl-fn apyrl) (cond [(empty? apyrl) . . . ] [else . . . (first (first apyrl)) . . . ; name of first . . . (second (first apyrl)) . . . ; salary of first . . . (my-apyrl-fn (rest apyrl)). . . ])) Example: a function name-list which consumes an alt-payroll and produces the corresponding list of names. CS 135 Fall 2009 05: Working with recursion 47 (define (name-list apyrl) (cond [(empty? apyrl) empty] [else (cons (first (first apyrl)) ; name of first (name-list (rest apyrl)))])) This code is less readable, because it uses only lists, instead of structures, and so is more generic-looking. We can fix this with a few definitions. CS 135 Fall 2009 05: Working with recursion 48 (define (name x) (first x)) (define (salary x) (second x)) (define (name-list apyrl) (cond [(empty? apyrl) empty] [else (cons (name (first apyrl)) (name-list (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 definition 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 ;; lookup-al: num al → (union string false) (define (lookup-al k alst) (cond [(empty? alst) false] ;; produces the value associated with k, or false if k not present [(equal? k (first (first alst))) (second (first alst))] [else (lookup-al 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 inefficient 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 define an alt-taxroll as a list of two-element lists, each sublist holding name and tax owed, we could reuse the name-list function to produce a list of names from an alt-taxroll. Our original structure-based payroll and taxroll definitions 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 defined 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 object-oriented 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 flat lists. We have just seen lists of lists in our example of lists containing two-element flat lists. In module 10 we will use lists containing unbounded flat 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 definition 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 defined 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 fixed-size 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.

Page1 / 20

05-recursion-post3up - Working with recursion Readings:...

This preview shows document pages 1 - 3. Sign up to view the full document.

View Full Document Right Arrow Icon
Ask a homework question - tutors are online