;; The first three lines of this file were inserted by DrRacket. They record metadata ;; about the language level of this file in a form that our tools can easily process. #reader(lib "htdp-beginner-reader.ss" "lang")((modname union-of-structs-after) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #f #t none #f () #f))) ;;; Data Definiton: ; A book is: (define-struct book (title author num-pages is-copyrighted?)) ; make-book : string string natnum boolean -> book ;;; Examples of the data (define b0 (make-book "Soul and Wit" "Barland" 0 #false)) (define b1 (make-book "The Cat in the Hat" "Seuss" 37 #true)) ;;; Template: ; func-for-book : book -> ... ; (define (func-for-book a-book) (... (book-title a-book) ... (book-author a-book) ... (book-num-pages a-book) ... (book-is-copyrighted? a-book))) ; reading-time : book -> non-negative real ; The amount of time it takes the average reader to read a book (in minutes) ; (define (reading-time a-book) (* (book-num-pages a-book) 2)) (check-expect (reading-time (make-book "The Cat in the Hat" "Seuss" 37 #true)) 74) (check-expect (reading-time b0) 0) ; book->string : book -> string ; Return a string-representation of a book struct, user-friendly. (define (book->string a-book) (string-append (book-title a-book) ", by " (book-author a-book) " (" (number->string (book-num-pages a-book)) "pp)" (if (book-is-copyrighted? a-book) ", ©" ""))) (check-expect (book->string (make-book "The Cat in the Hat" "Seuss" 37 #true)) "The Cat in the Hat, by Seuss (37pp), ©") (check-expect (book->string b0) "Soul and Wit, by Barland (0pp)") ; Create a brand new book, based on an original. ; The result's author will be the original author & the new-author, ; and the result has pages added to the original. ; (define (derive-from original co-author num-new-pages) (make-book (string-append "Son of " (book-title original)) (string-append (book-author original) " & " co-author) (+ (book-num-pages original) num-new-pages) #false)) (check-expect (derive-from b1 "Sauced" 3) (make-book "Son of The Cat in the Hat" "Seuss & Sauced" 40 #false)) (check-expect (derive-from b0 "Freund" 0) (make-book "Son of Soul and Wit" "Barland & Freund" 0 #false)) ;;; Data Def'n: (define-struct dvd (title year length rating)) ; make-dvd : string natnum real? symbol? -> dvd ; The length is in minutes. ;;; Examples of the data: (make-dvd "The Fellowship of the Ring" 2001 178 'PG-13) (define the-emoji-movie (make-dvd "The Emoji Movie" 2017 86 'PG)) ;;; Template ; func-for-dvd : dvd -> ... ; (define (func-for-dvd a-dvd) (... (dvd-title a-dvd) ... (dvd-year a-dvd) ... (dvd-length a-dvd) ... (dvd-rating a-dvd))) ;;; union of structs ;;; Datatype def'n: ; A lib-item is: ; - a book, OR ; - a dvd, OR ; - string [interpretation: a short description of an item, but no further info about it] ;; Examples of the data: (make-book "The Cat in the Hat" "Seuss" 37 #true) (make-dvd "The Fellowship of the Ring" 2001 178 'PG-13) "A fountain pen owned by Thomas Jefferson" "a Snickers wrapper" "a large unicorn plushie, for sitting on while reading" "somebody's old coffee cup" ; template: code for ANY function which processes a lib-item. ; func-for-lib-item : lib-item -> ...? ; (define (func-for-lib-item an-item) (cond [(book? an-item) (... (book-title an-item) (book-author an-item) (book-num-pages an-item) (book-copyrighted? an-item))] [(dvd? an-item) (... (dvd-title an-item) ... (dvd-year an-item) ... (dvd-length an-item) ... (dvd-rating an-item))] [(string? an-item) ...])) ;; N.B. we are NOT worrying about inheritance, and the fact that both books and dvds ;; have a field like "title" in common. It is a correct impulse/desire is to use ;; your O.O. design and have a superclass that both `book` and `dvd` might inherit from. ;; However, to keep things straight (and, to understand that we want to be able to ;; process union-types where branches might have *nothing* in common), we won't do ;; any inheritance. (You can look at full-rackets `define-struct` though, to see that ;; it actually can inherit fields, in real-life racket.) ; time-to-waste : lib-item -> non-negative real ; Return how much time I can waste from this library item, in minutes. (define (time-to-waste an-item) ; time-to-waste: a dvd's run time plus 5min to get it going, ; or (for a book) its reading time plus 2min to read back cover ; or (for other things) 1min to glance at it then ignore it. (cond [(book? an-item) (+ BOOK-STARTUP-TIME (reading-time an-item))] [(dvd? an-item) (+ (dvd-length an-item) DVD-STARTUP-TIME)] [(string? an-item) OTHER-WASTE-TIME])) (define DVD-STARTUP-TIME 5) (define BOOK-STARTUP-TIME 2) (define OTHER-WASTE-TIME 1) (check-expect (time-to-waste b0) (+ BOOK-STARTUP-TIME (reading-time b0))) (check-expect (time-to-waste (make-dvd "The Fellowship of the Ring" 2001 178 'PG-13)) (+ 178 DVD-STARTUP-TIME)) (check-expect (time-to-waste "a feather quill") 1) ; shelf-space-needed : lib-item -> non-negative real ; Return how much space is needed to shelve something, in the library. (define (shelf-space-needed an-item) (cond [(book? an-item) (+ (* (book-num-pages an-item) PAGE-THICKNESS) (* 2 (if (book-is-copyrighted? an-item) COVER-C-THICKNESS COVER-UNC-THICKNESS)))] [(dvd? an-item) (* DVD-BOX-THICKNESS (ceiling (/ (dvd-length an-item) (* 2 DISC-MAX-LENGTH))))] [(string? an-item) OTHER-SPACE-NEEDED])) ; librarians say: a book needs 1cm for every 250pp, plus the thickness ; of the two covers (which is 0.2cm ea. for non-copyrighted, and 0.5cm for copyrighted). ; librarians say: a dvd needs 1cm for each box (a single box holds up ; to two 180-min volumes). ; librarians say: other items are given 20cm of shelf-space, no matter what. ; (define PAGE-THICKNESS (/ 1 250)) ; in cm (define COVER-C-THICKNESS 0.5) ; thickness of book-cover (copyrighted), in cm (define COVER-UNC-THICKNESS 0.2) ; thickness of book-cover (uncopyrighted), in cm (define DVD-BOX-THICKNESS 1) ; in cm; one box holds up to two 180-min volumes. (define OTHER-SPACE-NEEDED 20) ; space needed to store 'other lib-itmes. (define DISC-MAX-LENGTH 180) ; max #min storable on a single DVD-disk. (check-expect (shelf-space-needed b0) (+ 0/250 (* 2 COVER-UNC-THICKNESS))) (check-expect (shelf-space-needed b1) (+ 37/250 (* 2 COVER-C-THICKNESS))) (check-expect (shelf-space-needed (make-dvd "The Fellowship of the Ring" 2001 178 'PG-13)) DVD-BOX-THICKNESS) (check-expect (shelf-space-needed (make-dvd "The Fellowship of the Ring Der Niebelungen" 1780 1799 'PG-25)) (* DVD-BOX-THICKNESS 5)) (check-expect (shelf-space-needed "a feather quill") OTHER-SPACE-NEEDED) ;;; If a union-of-structs meant a slightly increased template, then what about... ;;; struct-of-unions? ;;; union-of-unions? ;;; struct-of-structs? ;;;; Example of delegating, in a struct-of-structs ;; A collectors-edition contains a DVD *and* a book (the deluxe artwork hardback version, perhaps). ; TEMPLATE: same as ever: pull out the two fields. BUT STOP THERE; you'll call helpers ; rather than extract all its fields. ;;; Data Def'n: (define-struct collectors-edition (disc bk extra-art-pages)) ; make-collectors-edition : (-> dvd? book? natnum? collectors-edition) ;;; examples of the data ; (define tfotr-ce (make-collectors-edition (make-dvd "The Fellowship of the Ring" 2001 178 'PG-13) (make-book "The Fellowship of the Ring" "J.R.R. Tolkien" 324 #true) 15)) ;;; TEMPLATE ; func-for-ce : collectors-edition -> ... (define (func-for-ce a-ce) (... (collectors-edition-disc a-ce) (collectors-edition-bk a-ce) (collectors-edition-extra-art-pages a-ce))) ; ripoff : collectors-edition natnum -> collectors-edition ; ; derive-from the book by adding pages (all blank!), and also that many pages of extra-art, ; and replace the DVD with surplus copies of emoji movie (define (ripoff a-ce num-new-blank-pages) (make-collectors-edition the-emoji-movie (derive-from (collectors-edition-bk a-ce) "Anonymous" 10) (+ (collectors-edition-extra-art-pages a-ce) num-new-blank-pages))) (check-expect (ripoff tfotr-ce 10) (make-collectors-edition the-emoji-movie (derive-from (make-book "The Fellowship of the Ring" "J.R.R. Tolkien" 324 #true) "Anonymous" 10) (+ 15 10))) ; verify-title : collectors-edition -> boolean ; Do the book and dvd in the collectors-edition have the same name? (define (verify-title a-ce) ; Okay, consider calling a helper after pulling out the disc and bk. ; Notice that in this case, the helper happens to be just the getter! ; (Yeah, this kinda blurs the line between "don't process the fields", ; but that's okay, we can still appreciate the overall point.) (string=? (dvd-title (collectors-edition-disc a-ce)) (book-title (collectors-edition-bk a-ce)))) (check-expect (verify-title tfotr-ce) #true) (check-expect (verify-title (ripoff tfotr-ce 27)) #false) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; TODO: a struct-of-unions. ; [Template: Call a helper; ; don't have a cond inside the struct-processing program.]