580 likes | 596 Views
Learn how to define and work with custom data types in Scheme programming language through structured steps and examples. Explore the process of creating, naming, accessing, and using new data structures effectively.
E N D
TeachScheme, ReachJava Adelphi University Tuesday afternoon June 23, 2009
Defining our own structs • Choose a name for the new data type • Choose names and types for each of its parts • Write a define-struct (next slide) to tell Scheme about it • Write contracts for the constructor and accessor methods • Write examples of the new data type • Write a function template for the new data type • Start writing functions on the new data type
Syntax Rule: Defining a Struct To define a structure named foo with fields snark and boojum, (define-struct foo (snark boojum)) This defines a new data type foo and several functions: ; make-foo : snark-type boojum-type -> foo ; foo-snark : foo -> snark-type ; foo-boojum : foo -> boojum-type ; foo? : anything -> boolean
Example: posn (if it weren't pre-defined) ; A posn consists of two numbers (x and y) (define-struct posn (x y)) ; make-posn : number number -> posn ; posn-x : posn -> number ; posn-y : posn -> number ; posn? : anything -> boolean "Examples of the posn data type:" (make-posn 3 5) (make-posn 12 -7) (define here (make-posn 14 9)) (check-expect (posn-x here) 14) (check-expect (posn-y here) 9)
Real example: dogs ; A dog has a string(name), number(age), number(weight), and a boolean(asleep?) (define-struct dog (name age weight asleep?)) ; make-dog : string num num boolean -> dog ; dog-name : dog -> string ; dog-age : dog -> number ; dog-weight : dog -> number ; dog-asleep? : dog -> boolean
Real example: dogs "Examples of the dog data type:" (make-dog "Ludo" 6 78 true) (make-dog "Thibaut" 4 74 false) (define this-dog (make-dog "Rover" 8 45 false)) (check-expect (dog-name this-dog) "Rover") (check-expect (dog-age this-dog) 8) (check-expect (dog-weight this-dog) 45) (check-expect (dog-asleep? this-dog) false)
Type-based coding patterns Almost any function that takes in a dog looks like (define (function-on-dog the-dog) ) (the "skeleton" step of the recipe)
Type-based coding patterns Almost any function that takes in a dog looks like (define (function-on-dog the-dog) ; the-dog a dog ) (the "inventory" step of the recipe)
Type-based coding patterns Almost any function that takes in a dog looks like (define (function-on-dog the-dog) ; the-dog a dog ; (dog-name the-dog) a string ; (dog-age the-dog) a number ; (dog-weight the-dog) a number ; (dog-asleep? the-dog) a boolean ) (the "inventory" step of the recipe, continued)
Type-based coding patterns #| (define (function-on-dog the-dog) ; the-dog a dog ; (dog-name the-dog) a string ; (dog-age the-dog) a number ; (dog-weight the-dog) a number ; (dog-asleep? the-dog) a boolean ) |# This header-plus-inventory, commented out, can be used as a template, copying and pasting it as a starting point for any function that takes in a dog.
A function on dogs Write a function fits-in-lap? which takes in a dog and the weight capacity of a lap, and tells whether the dog will fit in the lap in question Work this out together
Your turn Write a function movable? which takes in a dog. If the dog is awake, it's movable. If the dog is asleep but under 20 pounds, it's movable. Otherwise it's not. (Hint: it's shorter and simpler if you don't use a cond.) Write a function birthday which takes in a dog and returns a dog with the same name, weight, and sleep status, but one year older.
Inventors and Factories define-struct is like an inventor.Specifies once what's in a particular model of cell phone, but doesn't actually manufacture them. Then invents something else, and doesn't manufacture them either…. make-posn is like a cell-phone factory.Each factory "knows" how to build one kind of thing (cell phones, posns, dogs, etc.)Factory doesn't exist until the thing is invented.Can be used over and over to build many cell-phones/posns/dogs/whatever.
Defining another animal Define a data structure fish which has a string(color), a number(weight) and a boolean(salt-water?). Remember the steps: • Choose a name for the new data type • Choose names and types for each of its parts • Write a define-struct to tell Scheme about it • Write contracts for the constructor and accessor methods • Write examples of the new data type • Write a function template for the new data type • Start writing functions on the new data type
Definition by choices New data type: animal ; An animal is either a dog or a fish. Scheme doesn't enforce this; it's up to the programmer.
A function on animals Write a function fits-in-crate? which takes in an animal (either a dog or a fish) and the weight capacity of a crate, and tells whether the animal can be shipped in that crate. Hint: fish are never shipped in crates, regardless of weight.
How to write this? The input type, animal, is one of two sub-categories (dog or fish), so… • we need at least two test cases, one of each type (in fact, we'll need three dogs — under, over, and borderline — and at least one fish) • the function body will probably be a cond with two cases, with questions "dog?" and "fish?"
Function template for animals Almost any function on animals will look like (define (function-on-animal the-animal) )
Function template for animals Almost any function on animals will look like (define (function-on-animal the-animal) (cond [(dog? the-animal) ] [(fish? the-animal) ]))
Function template for animals Almost any function on animals will look like (define (function-on-animal the-animal) (cond [(dog? the-animal) ; the-animal a dog ] [(fish? the-animal) ; the-animal a fish ]))
Function template for animals #| (define (function-on-animal the-animal) (cond [(dog? the-animal) ; the-animal a dog ; (dog-name the-animal) a string ; (dog-age the-animal) a number ; (dog-weight the-animal) a number ; (dog-asleep? the-animal) a boolean ] [(fish? the-animal) ; the-animal a fish ; (fish-color the-animal) a string ; (fish-weight the-animal) a number ; (fish-salt-water? the-animal) a boolean ])) |#
Function template for animals Again, we can copy and paste this as a starting point for any function on animals. In practice, much of it is irrelevant to any given function, so we can delete those lines.
My answer to fits-in-crate? ; fits-in-crate? : animal number -> boolean (define (fits-in-crate? the-animal max-weight) ; max-weight a number (cond [(dog? the-animal) ; (dog-weight the-animal) a number (<= (dog-weight the-animal) max-weight) ] [(fish? the-animal) false ])) "Examples of fits-in-crate?:" (check-expect (fits-in-crate? (make-dog "Bob" 3 58 true) 50) false) (check-expect (fits-in-crate? (make-dog "Dave" 2 65 true) 65) true) ; borderline (check-expect (fits-in-crate? (make-dog "Eddie" 7 35 false) 50) true) (check-expect (fits-in-crate? (make-fish "orange" 0.03 false) 5) false)
Another exercise Write a function underweight? which takes in an animal and tells whether or not it's underweight — which means under 30 pounds for a dog, and under 0.1 pounds for a fish.
Lab exercise • Open shapes-lab.scm (in Examples folder) • Do the exercises in it
Review: kinds of data types Primitive data types number, string, boolean, image, … Types defined by their parts posn, dog, fish, circle, rectangle, … Types defined as one of several choices subranges of numbers, animal, shape, …
Review: definition by parts ; A fish has a string(color), number(weight), and boolean(salt-water?) (define-struct fish (color weight salt-water?)) ; constructor, getters, and discriminator defined automagically Testing pattern:call (make-fish …) to create as many instances as necessary Coding pattern:(define (function-on-fish the-fish) ; the-fish fish ; (fish-color the-fish) string ; (fish-weight the-fish) number ; (fish-salt-water? the-fish) boolean )
Review: definition by choices ; An animal is either a dog, a fish, or a wombat Not enforced by language; programmer convention Testing pattern:at least one example for each choice Coding pattern:(define (function-on-animal the-animal) (cond [(dog? the-animal) ; the-animal a dog ] [(fish? the-animal) ; the-animal a fish ] [(wombat? the-animal) ; the-animal a wombat ]))
Just to make sure… … please take some time to work the shapes-lab exercises. When are you defining a type by parts? When are you defining a type by choices? How do you write a function on a type defined by parts? How do you write a function on a type defined by choices?
New topic: lists What is a list? Examples: shopping list list of students in class list of animals in zoo Properties: can hold many objects, or one, or even none number of objects may not be known in advance objects are in one-after-another sequence
So what is a list? • A list is either empty or not.Definition by choices: empty or not? • If it's not empty, that means it has at least one thing in it, so there's a "first" thing. There's also "the rest" of the list, which might or might not be empty…. i.e. a listDefinition by parts: first and rest.
One way to define lists ; A list is either empty or non-empty. ; Definition by choices: (define (function-on-list the-list)(cons [(empty? the-list) ; the-list an empty list ] [(non-empty? the-list) ; the-list a non-empty list ]))
One way to define lists ; An empty list has no parts at all. Definition by (no) parts. (define-struct empty []) ; make-empty: nothing -> empty ; empty? : object -> boolean ; A non-empty list has a first element and a rest which is itself a (possibly empty) list. Definition by parts. (define-struct non-empty [first rest]) ; make-non-empty : object list -> non-empty ; non-empty-first : non-empty -> object ; non-empty-rest : non-empty -> list ; non-empty? : object -> boolean
One way to define lists ; An empty list has no parts at all. Definition by parts, sorta. (define-struct empty []) … ; A non-empty list has a first element and a rest which is itself a (possibly empty) list. Definition by parts. (define-struct non-empty [first rest]) … "Examples of lists:" (make-empty) (make-non-empty 3 (make-empty)) (make-non-empty 4 (make-non-empty 3 (make-empty))) (make-non-empty "Joe" (make-non-empty "Mary" (make-empty)))
The way we really do it • This is a pain. • Lists are so common that Scheme provides predefined functions to handle them. • emptyis a predefined constant, liketrueandfalse • consis short for "construct": it constructs longer lists from shorter ones. (Equivalent to make-non-empty) ; cons : object list -> non-empty-list ; first : non-empty-list -> object ; rest : non-empty-list -> list ; empty? : object -> boolean ; cons? : object -> boolean
The way we really do it "Examples of lists:" empty (cons 3 empty) (cons 4 (cons 3 empty)) (cons "Joe" (cons "Mary" empty)) There is an even briefer notation, which we'll get to later.
Examples of working with lists (define list1 (cons "Joe" empty)) (define list2 (cons "Mary" list1)) (define list3 (cons "Bob" list2)) (define list4 (cons "Phil" (cons "Amy" empty))) (define list5 (cons 3 (cons 6 (cons -5 (cons 3 empty))))) (check-expect (empty? list1) false) (check-expect (cons? list1) true) (check-expect (first list1) "Joe") (check-expect (first list2) "Mary") (check-expect (first list3) "Bob") (check-expect (first list4) "Phil") (check-expect (first list5) 3)
Examples of working with lists (check-expect (rest list1) empty) (check-expect (rest list2) (cons "Joe" empty)) (check-expect (rest list3)(cons "Mary" (cons "Joe" empty))) (check-expect (rest list4) (cons "Amy" empty)) (check-expect (first (rest list4)) "Amy") (check-expect (first (rest (rest list5))) -5)
Template using choices and parts (define (function-on-list the-list) (cond [(empty? the-list) ; the-list an empty list ] [(cons? the-list) ; the-list a non-empty list ; (first the-list) whatever type is in the list ; (rest the-list) a list in its own right ]))
Template using choices and parts (define (function-on-list the-list) (cond [(empty? the-list) ; the-list an empty list ] [(cons? the-list) ; the-list a non-empty list ; (first the-list) whatever type is in the list ; (rest the-list) a list in its own right ;(function-on-list (rest the-list)) ]))
Shape of data -> shape of code! The data type was self-referential in the "rest" part of the "non-empty" case, so… the function is self-referential in the "rest" part of the "non-empty" case. Recursion has sneaked in; nothing new, just applying the coding patterns we already know. Students don't know recursion is hard unless you tell them. This form of recursion cannot go infinite!
Writing functions on lists ; count-elements : list -> number
Writing functions on lists ; count-elements : list -> number "Examples of count-elements:" (check-expect (count-elements empty) 0) (check-expect (count-elements (cons "Joe" empty)) 1) (check-expect (count-elements (cons "Phil" (cons "Joe" empty))) 2)
Writing functions on lists ; count-elements : list -> number (define (function-on-list the-list) (cond [(empty? the-list) ; the-list an empty list ] [(cons? the-list) ; the-list a non-empty list ; (first the-list) whatever type is in the list ; (rest the-list) a list in its own right ;(function-on-list (rest the-list)) ])) "Examples of count-elements:" (count-elements empty) "should be" 0 (count-elements (cons "Joe" empty)) "should be" 1 (count-elements (cons "Phil" (cons "Joe" empty))) "should be" 2
Writing functions on lists ; count-elements : list -> number (define (count-elements the-list) (cond [(empty? the-list) ; the-list an empty list ] [(cons? the-list) ; the-list a non-empty list ; (first the-list) whatever kind of object is in the list ; (rest the-list) a list in its own right ; (count-elements (rest the-list)) a number ])) "Examples of count-elements:" …
Writing functions on lists ; count-elements : list -> number (define (count-elements the-list) (cond [(empty? the-list) ; the-list an empty list 0 ] ; from one of the test cases [(cons? the-list) ; the-list a non-empty list ; (first the-list) whatever kind of object is in the list ; (rest the-list) a list in its own right ; (count-elements (rest the-list)) a number ])) "Examples of count-elements:" …
Writing functions on lists ; count-elements : list -> number (define (count-elements the-list) (cond [(empty? the-list) ; the-list an empty list 0 ] [(cons? the-list) ; the-list a non-empty list ; (first the-list) whatever kind of object is in the list ; (rest the-list) a list in its own right (+ 1 (count-elements (rest the-list))) ])) "Examples of count-elements:" …
Now you try one ; add-up : list of numbers -> number "Examples of add-up:" (check-expect (add-up empty) 0) (check-expect (add-up (cons 3 empty)) 3) (check-expect (add-up (cons 4 (cons 3 empty))) 7)
Searching • We represent the ingredients on a pizza as a list of strings, e.g. • empty • (cons "cheese" empty) • (cons "garlic" (cons "cheese" empty)) • (cons "pepperoni" (cons "garlic" (cons "cheese" empty))) • We want to know whether there are any onions on the pizza.
Searching ; has-onions? : list-of-strings -> boolean "examples of has-onions?:" (check-expect (has-onions? empty) false) (check-expect (has-onions? (cons "cheese" empty)) false) (check-expect (has-onions? (cons "onions" empty)) true) (check-expect (has-onions? (cons "cheese" (cons "onions" (cons "pepperoni" empty)))) true) (check-expect (has-onions? (cons "cheese" (cons "garlic" (cons "pepperoni" empty)))) false)