1 / 83

Introduction to Computer Science I Topic 3: Recursive Data Types and Structural Recursion

Introduction to Computer Science I Topic 3: Recursive Data Types and Structural Recursion. Prof. Dr. Max Mühlhäuser Dr. Guido Rößling. Lists. Structures are useful for storing data objects that contain a fixed number of elements

preston
Télécharger la présentation

Introduction to Computer Science I Topic 3: Recursive Data Types and Structural Recursion

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Introduction to Computer Science ITopic 3: Recursive Data Types and Structural Recursion Prof. Dr. Max MühlhäuserDr. Guido Rößling

  2. Lists • Structures are useful for storing data objects that contain a fixed number of elements • However, in many cases we do not know how many elements a data structure consists of • or the structure of the data is recursive • Using recursive data types, we can store data objects of arbitrary size in a structured way • Idea: One element of the data structure stores (directly or indirectly) another instance of the data structure • This is what we call a recursive data structure • A recursion anchor is needed for a finite data structure • To build this recursion anchor, we use the method for heterogeneous data from our last lecture

  3. Building Recursive Data Types • A list lst is either • the empty list, the-emptylst, or • (make-lst s r), where s is a value and r is a list

  4. Lists • Building lists with structures (define-structlst (first rest)) (define-structemptylst ()) (define the-emptylst (make-emptylst)) ;; a list with 0 elements (define list0 the-emptylst) ;; a list with 1 element (define list1 (make-lst 'a the-emptylst)) ;; a list with 2 elements (define list2 (make-lst 'a (make-lst 'b the-emptylst))) ;; get the 2nd element from list2 (lst-first (lst-rest list2))  'b

  5. Lists • Lists are an important data type and therefore there is a built-in data type for them • (also because of historical reasons) • Constructor with 2 arguments: cons (for construct) • equivalent to make-lst from previous slide • The empty list: empty • equivalent to the-emptylst • Selectors: first for the first element, rest for the second element • equivalent to lst-first, lst-rest • “historical” names for first and rest: car and cdr • Predicates: list?, empty? • equivalent to lst?, emptylst?

  6. Lists • Example ;; a list with 0 elements ;; (define list0 the-emptylst) (define list0 empty) ;; a list with 1 element ;; (define list1 (make-lst 'a the-emptylst)) (define list1 (cons 'a empty)) ;; a list with 2 elements ;; (define list2 (make-lst 'a ;; (make-lst 'b the-emptylst))) (define list2 (cons 'a (cons 'b empty))) ;; get the 2nd element from list2 ;; (lst-first (lst-rest list2))  'b (first (rest list2))  'b

  7. Lists • The only difference between make-lstand cons: • cons expects empty or (cons … ) as second argument • E.g. (cons 1 2) throws an error, (make-lst 1 2) doesn’t • Thus, cons prevents incorrect use • however, this check is missing in other versions of Scheme • A better emulation would look like this: • But this can not avoid that the user uses make-lstdirectly (define-structlst (first rest)) (define-structemptylst ()) (define the-emptylst (make-emptylst)) (define (our-cons a-value a-list) (cond [(emptylst? a-list) (make-lst a-value a-list)] [(lst? a-list) (make-lst a-value a-list)] [else (error 'our-cons "list as second argument expected")]))

  8. Lists

  9. Lists • In general, a list does not have to contain values of one kind, but may contain arbitrary values (cons 0 (cons 1 (cons 2 (cons 3 (cons 4 (cons 5 (cons 6 (cons 7 (cons 8 (cons 9 empty)))))))))) (cons 'RobbyRound (cons 3 (cons true empty)))

  10. The Closure Property • An operation for combining data objects satisfies the closure property if the results of combining things with that operation can themselves be combined using the same operation • Example: cons • Suchcombination operators permit us to create hierarchical data structures.

  11. The Closure Property Question: Have we seen the closure property before?

  12. The Closure Property • Origin of the word „closure“ • Abstract algebra: A set of elements is said to be closed under an operation, if applying the operation to elements in the set produces an element that is again an element of the set

  13. The Closure Property • The thought that an instrument of combination satisfies the closure property is very intuitive • Unfortunately, the combination operators of many languages do not satisfy closure • We can combine elements by storing them in an array • However, in some languages one cannot build arrays of arrays or not use them as parameter/return types • Lack of a built-in general-purpose “glue” that makes it easy to manipulate compound data in a uniform way

  14. Thelist Constructor • Creating longer lists with cons is somewhat tedious • Therefore there is a constructor called list • accepts any number of arguments • creates a list with all arguments as elements • e.g. (list 1 2 3) instead of (cons 1 (cons 2 (cons 3 empty))) • e.g. (list (list ‘a 1) (list ‘b 2)) • In general (list exp-1 … exp-n) is equivalent to (cons exp-1 (cons … (cons exp-n empty ) … ))

  15. The ‘ / quote Constructor • Lists and symbols can be abbreviated even more by using the quote constructor ' • '(1 2 3) is short for (list 1 2 3) • '((1 2) (3 4) (5 6)) stands for(list (list 1 2) (list 3 4) (list 5 6)) • '(a b c) stands for (list 'a 'b 'c) • Don’t confuse this with (list a b c) – list evaluates all arguments,quote does not • In general, '(exp-1…exp-n) is equivalent to(list 'exp-1… 'exp-n), where 'exp-i = exp-ifor all self-evaluating values(numbers, booleans) • Note that this rule is recursive! • To use list and quote, • please use the level “Beginning Student with List Abbreviations” in DrScheme from now on!

  16. Handling recursive data types • How do we handle instances of recursive data types? • Answer: We use our design method for heterogeneous data • Example: Does a special symbol occur in a list of symbols? • 1st Step: Definition of the contract, Header etc. • Note the convention list-of-XXX to document in the contract what kind of data is expected as list elements ;; contains-doll? : list-of-symbols -> boolean ;; to determine whether the symbol 'doll occurs ;; on a-list-of-symbols (define (contains-doll? a-list-of-symbols) ...)

  17. Handling recursive data types • Creating templates • Write a cond-branch for each data type; indicate selectors • First Case (empty list) is trivial (define (contains-doll? a-list-of-symbols) (cond [(empty? a-list-of-symbols) ...] [(cons? a-list-of-symbols) ... (first a-list-of-symbols) ... ... (rest a-list-of-symbols) ...])) (define (contains-doll? a-list-of-symbols) (cond [(empty? a-list-of-symbols) false] [(cons? a-list-of-symbols) ... (first a-list-of-symbols) ... ... (rest a-list-of-symbols) ...]))

  18. Handling recursive data types • We can already test the first element of the list with the available data • What do we do with the rest of the list? • We need an auxiliary procedure, which tests if the (rest of the) list contains the symbol • This auxiliary procedure is contains-doll? itself! (define (contains-doll? a-list-of-symbols) (cond [(empty? a-list-of-symbols) false] [(cons? a-list-of-symbols) (cond [(symbol=? (first a-list-of-symbols) 'doll) true] ... (rest a-list-of-symbols) ...]))

  19. Handling recursive data types • Solution: (define (contains-doll? a-list-of-symbols) (cond [(empty? a-list-of-symbols) false] [(cons? a-list-of-symbols) (cond [(symbol=? (first a-list-of-symbols) 'doll) true] [else (contains-doll? (rest a-list-of-symbols))]) ]))

  20. Handling recursive data types • Why is it this solution well-defined? • Here, well-defined means that the evaluation of the procedure terminates • Not every recursive definition leads to a well-defined function! • e.g. (define (f a-bool) (f (not a-bool))) • Our recursive definition is an example of structural recursion • The structure of the procedure follows the (recursive) structure of the data • Such recursive definitions are always well defined, because the nesting depth of the data is reduced after each recursive call (define (contains-doll? a-list-of-symbols) (cond [(empty? a-list-of-symbols) false] [(cons? a-list-of-symbols) (cond [(symbol=? (first a-list-of-symbols) 'doll) true] [else (contains-doll? (rest a-list-of-symbols))]) ]))

  21. Design of procedures for recursive data • How does our design concept change? • Data analysis and design • If the problem description involves information of arbitrary size, we need a recursive data structure • The definition of this data structure needs at least two cases, at least one of which must not (directly or indirectly) refer back to the definition • Contract and procedure header: no changes • Examples: no changes • Template: Indicate the recursive callin the recursive branches • Procedure body • First, implement the basic (non-recursive) branches, then the recursive branches. For the recursive calls, assume that the procedure already works as desired • Test: no changes

  22. Creating recursive data • Procedures that process recursive data according to our design method usually combine the result of recursive call(s) with the non-recursive data • This combination often consists of the construction of new data which has the same structure as the input (define (sum a-list-of-nums) (cond [(empty? a-list-of-nums) 0] [else (+ (first a-list-of-nums) (sum (rest a-list-of-nums)))]))

  23. Creating recursive data • Computation of the wage • of a person or multiple people ;; wage : number -> number ;; to compute the total wage (at $12 per hour) ;; of someone who worked for h hours (define (wage h) (* 12 h)) ;; hours->wages : list-of-numbers -> list-of-numbers ;; to create a list of weekly wages from ;; a list of weekly hours (alon) (define (hours->wages alon) (cond [(empty? alon) empty] [else (cons (wage (first alon)) (hours->wages (rest alon)))]))

  24. Structures that contain Structures • Structures (and lists) do not necessarily have to contain atomic data • They can e.g. contain instances of their own structure as elements (as in the list structure) • They may also contain any other types of structures/lists • e.g. list of points, list of lists of points • Example: • A inventory record is a structure (make-ir s n),s being a symbol and n a (positive) number:(define-structir (name price)) • An inventory-list is either • empty, or • (cons ir inv), ir being an inventory record and inv an inventory-list

  25. The Structure of Natural Numbers • Often, natural numbers are described by means of an enumeration: 0,1,2, etc. • However, we cannot program “etc.” • Using a recursive definition, we can get rid of “etc.”: • 0 is a natural number • If n is a natural number, then so is the successor of n(succ n) • This definition generates: 0, (succ 0), (succ (succ 0)) etc. • Cf. the Peano-Axioms from mathematics • This is analogous to the construction of lists • cons is equivalent to succ • rest is equivalent to pred • empty? is equivalent to zero? • Therefore, functions that operate on natural numbers and functions that operate on lists can be structured in the same way!

  26. The Structure of Natural Numbers • Here is the definition of the new procedures pred and succ: ;; pred: N -> N ;; returns the predecessor of n; if n is 0, return 0 (define (pred n) (if (> n 0) (- n 1) 0 ) ) ;; succ: N -> N ;; returns the successor of n; if negative, return 0 (define (succ n) (if (> n -1) (+ n 1) 0 ) )

  27. The Structure of Natural Numbers • Example 1 • Example 2 ;; hellos : N -> list-of-symbols ;; to create a list of n copies of 'hello (define (hellos n) (cond [(zero? n) empty] [else (cons 'hello (hellos (pred n)))])) ;; ! : N -> N ;; computes the faculty function (define (! n) (cond [(zero? n) 1] [else (* n (! (pred n)))]))

  28. IntermezzoDesign of auxiliary procedures revisited

  29. Design of auxiliary procedures: wish lists • Larger programs are made up of many different (auxiliary) procedures • Suggested method: Creation and maintenance of a wish list of auxiliary procedures • A list of procedures that are missing to complete a program • The procedures of the wish list can be implemented stepwise by means of our design method • The wish list will often change during implementation • e.g. one discovers that new auxiliary procedures are needed

  30. Design of auxiliary procedures: wish lists The order of processing the wish list is important • “Bottom-Up”-approach: First implement the procedures that do not depend on any other procedure of the wish list • Advantage: We can test immediately and at any time • Disadvantage: At the beginning it is often difficult to see which procedures are needed on the lowest level; one might lose track of the “big picture” • “Top-Down”- approach: First implement the main procedure, then those called by the main procedure, and so on • Advantage : Incremental refinement of the program structure • Disadvantage : Testing can only be done late; sometimes you discover way “down” that there is a conceptual error “higher up” • Often a mix of Top-Down and Bottom-Up makes sense

  31. Design of auxiliary procedures: wish lists • Example: Sorting a list of numbers • First, we follow our design method… ;; sort : list-of-numbers -> list-of-numbers ;; to create a sorted list of numbers from all;; the numbers in alon ;; Examples: ;; (sort empty)  empty ;; (sort (cons 1297.04 (cons 20000.00 (cons -505.25 empty)))) ;;  (cons -505.25 (cons 1297.04 (cons 20000.00 empty))) (define (sort alon) (cond [(empty? alon) ...] [else ... (first alon) ... (sort (rest alon)) ...]))

  32. Design of auxiliary procedures: wish lists • First case (empty list) is trivial • The call (sort (rest alon)) returns a sorted list in the recursive case • (first alon) has to be sorted into this list • The procedure for doing this is added to our wish list; for now we can complete the sort-procedure ;; insert : number list-of-numbers -> list-of-numbers ;; to create a list of numbers from n and the numbers ;; in alon that is sorted in descending order; alon is ;; already sorted (define (insert n alon) ...) (define (sort alon) (cond [(empty? alon) empty] [else (insert (first alon) (sort (rest alon)))]))

  33. Design of auxiliary procedures: wish lists • The insert-procedure can now be implemented independently of sort • important: detailed analysis of the different cases • This type of sorting is known as Insertion-Sort ;; insert : number list-of-numbers (sorted) ;; -> list-of-numbers (sorted) ;; to create a list of numbers from n and the numbers on ;; alon that is sorted in descending order; alon is sorted (define (insert n alon) (cond [(empty? alon) (cons n empty)] [else (cond [(<= n (first alon)) (cons n alon)] [(> n (first alon)) (cons (first alon) (insert n (rest alon)))])]))

  34. Design of auxiliary procedures: Generalization of Problems • A more general problem is often easier to solvethan the actual problem • Approach: The auxiliary procedure solves the generalized problem, the main procedure specializes the auxiliary procedure to solve the actual problem • Example: Changing coins • In how may different ways can we make change of 1 €, given coins of 1, 2, 5, 10, 20 and 50 Cent? • Generalization: In how may different ways can we changeamountausingthefirstntypesofcoinsfrom1, 2, 5, 10, 20 and 50 Cent? • Specialization: n = 6

  35. Design of auxiliary procedures: Generalization of Problems • Counting ways to change: Straightforward formulation of the generalized problem as recursive procedure: • The number of ways to change amount a using n kinds of coins equals: • 1 if a = 0 • 0 if a<0 or n= 0 • otherwise: • the number of ways to change amount a using all but the last kind of coin, plus • the number of ways to change amount a-d using all n kinds of coins, where d is the denomination of the last kind of coin.

  36. Design of auxiliary procedures: Generalization of Problems (define (count-change amount) (cc amount 6)) (define (cc amount kinds-of-coins) (cond [(= amount 0) 1] [(or (< amount 0) (= kinds-of-coins 0)) 0] [else (+ (cc amount (- kinds-of-coins 1)) (cc (- amount (denomination kinds-of-coins)) kinds-of-coins))])) (define (denomination coin-number) (cond ((= coin-number 1) 1) ((= coin-number 2) 2) ((= coin-number 3) 5) ((= coin-number 4) 10) ((= coin-number 5) 20) ((= coin-number 6) 50)))

  37. Design of auxiliary procedures: Generalization of Problems • Comments on the count-change procedure: • This kind of recursion is different from structural recursion, which you have seen before • More on this later • This procedure needs a lot of computing • A lot of time may be needed for amounts >100 • The time required increases exponentially with the size of the amount • More on this later

  38. Trees

  39. Trees • Trees are one of the most important data structures in computer science • representation of hierarchical relations • Example: family trees Carl (1926) Eyes: green Bettina (1926) Eyes: green Adam (1960) Eyes: yellow Dave (1955) Eyes: black Eva (1965) Eyes: blue Fred (1966) Eyes: pink Gustav (1988) Eyes: brown

  40. Trees • Data modeling, 1st attempt: • Problem: It is impossible to create an instance of this data structure • The basic case is missing (termination of the recursion) • We failed to apply our rule for designing recursive data structures • The definition of a recursive data structure needs at least two cases, at least one of which must not be recursive • A child is a structure(make-child father mother name date eyes), • wherefatherandmotherare child-structures; nameandeyesare symbols, dateis a number

  41. Trees • Data modeling, 2nd attempt : • This definition conforms to our design rule; it can represent a family tree • A family tree node (ftn) is either • 1. empty, or • 2.(make-child father mother name year eyes) • wherefatherandmotherare ftn;name and eyesare symbols, year is a number

  42. Trees • Carl: (make-child empty empty ‘Carl 1926 ‘green) • Adam: (make-child (make-child empty empty ‘Carl 1926 ‘green) (make-child empty empty ‘Bettina 1926 ‘green)‘Adam 1950 yellow) Carl (1926) Eyes: green Bettina (1926) Eyes: green redundancy! Adam (1960) Eyes: yellow Dave (1955) Eyes: black Eva (1965) Eyes: blue Fred (1966) Eyes: pink Gustav (1988) Eyes: brown

  43. Trees • Avoid redundancy by binding to names ;; Oldest Generation: (define Carl (make-child empty empty 'Carl 1926 'green)) (define Bettina (make-child empty empty 'Bettina 1926 'green)) ;; Middle Generation: (define Adam (make-child Carl Bettina 'Adam 1950 'yellow)) (define Dave (make-child Carl Bettina 'Dave 1955 'black)) (define Eva (make-child Carl Bettina 'Eva 1965 'blue)) (define Fred (make-child empty empty 'Fred 1966 'pink)) ;; Youngest Generation: (define Gustav (make-child Fred Eva 'Gustav 1988 'brown))

  44. Trees • Design of functions on trees • The method is the same, but it leads to more than one recursive call • General template ;; fun-for-ftn : ftn -> ??? (define (fun-for-ftn a-ftree) (cond [(empty? a-ftree) ...] [else ... (fun-for-ftn (child-father a-ftree)) ... ... (fun-for-ftn (child-mother a-ftree)) ... ... (child-name a-ftree) ... ... (child-date a-ftree) ... ... (child-eyes a-ftree) ...]))

  45. Trees • Example: blue-eyed-ancestor? ;; blue-eyed-ancestor? : ftn -> boolean ;; to determine whether a-ftree contains a child ;; structure with 'blue in the eyes field (define (blue-eyed-ancestor? a-ftree) (cond [(empty? a-ftree) false] [else (or (symbol=? (child-eyes a-ftree) 'blue) (or (blue-eyed-ancestor? (child-father a-ftree)) (blue-eyed-ancestor? (child-mother a-ftree))))]))

  46. Mutually Recursive Structures • Inverted example: Children instead of ancestors Carl (1926) Eyes: green Bettina (1926) Eyes: green Adam (1960) Eyes: yellow Dave (1955) Eyes: black Eva (1965) Eyes: blue Fred (1966) Eyes: pink Gustav (1988) Eyes: brown Important difference for modeling: A person canhave any number of children, but only two ancestors (parents)  We need a list to store the children

  47. Mutually Recursive Structures • We need two separate data structures: • A parent is a structure (make-parent children name date eyes), wherechildren is a list of children, name and eyes symbols, and date is a number. • A list of children is either • empty, or • (cons p children), whereas p is a parent and children is a list of children • Each of these definitions is useless when looked at separately, because it refers to another data structure which has not yet been defined • Such data structures are called mutually recursive • Mutually recursive data structures should always be defined together

  48. Mutually Recursive Structures • Representation of the family tree ;; Youngest Generation: (define Gustav (make-parent empty 'Gustav 1988 'brown)) (define Fred&Eva (list Gustav)) ;; Middle Generation: (define Adam (make-parent empty 'Adam 1950 'yellow)) (define Dave (make-parent empty 'Dave 1955 'black)) (define Eva (make-parent Fred&Eva 'Eva 1965 'blue)) (define Fred (make-parent Fred&Eva 'Fred 1966 'pink)) (define Carl&Bettina (list Adam Dave Eva)) ;; Oldest Generation: (define Carl (make-parent Carl&Bettina 'Carl 1926 'green)) (define Bettina (make-parent Carl&Bettina 'Bettina 1926 'green))

  49. Mutually Recursive Structures • How do we create the procedure blue-eyed-descendant? • Contract, description, header, examples: ;; blue-eyed-descendant? : parent -> boolean ;; to determine whether a-parent or any of its descendants ;; (children, grandchildren, and so on) have 'blue ;; in the eyes field (define (blue-eyed-descendant? a-parent) ...) ;; Here are three simple examples, formulated as tests: (boolean=? (blue-eyed-descendant? Gustav) false) (boolean=? (blue-eyed-descendant? Eva) true) (boolean=? (blue-eyed-descendant? Bettina) true)

  50. Mutually Recursive Structures • Template: • Current person can be checked immediately (define (blue-eyed-descendant? a-parent) ... (parent-children a-parent) ... ... (parent-name a-parent) ... ... (parent-date a-parent) ... ... (parent-eyes a-parent) ... ) (define (blue-eyed-descendant? a-parent) (cond [(symbol=? (parent-eyes a-parent) 'blue) true] [else ... (parent-children a-parent) ... ... (parent-name a-parent) ... ... (parent-date a-parent) ...])) Name and date of birth are unimportant for this task

More Related