;; 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 start-design-recipe-after) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #f #t none #f () #f))) #| `define`: a keyword to bind an identifier to a value: |# (define (natural? n) (and (integer? n) (positive? n))) (define (non-negative? n) (and (real? n) (not (negative? n)))) (define n 37) (define m (* n 99)) (define ϕ (/ (+ 1 (sqrt 5)) 2)) (define fname "Ian") (define lname "B-meister") ; Syntax for define-a-variable: ; (define ) ; Syntax for define-a-function: ;(define ( …) ; ) #| The Design Recipe (take 1 -- primitive types only) ------- per function: 4. tests (consider: 0,1,many; strings/lists/arrays/collections of length 0,1,many; fractions & negatives 5. SSS: signature, purpose-statement, and stub (purpose-statement should mention each arg by name; always mention units if there are any) 7. complete the body-expression 8. watch your tests pass |# ;;;;;;;;;;;; Example, of design recipe ;;;;;;;;;;;;;;;;;;;;; (check-within (pizza-area 18) 254.5 0.1) ; Make a few more tests! (check-expect (pizza-area 0) 0) (check-within (pizza-area 2) 3.14159 0.00001) (check-within (pizza-area 1) (/ pi 4) 0.000001) (check-within (pizza-area 0.5) (* pi 0.25 0.25) 0.00001) ; pizza-area must be given a non-negative number ; `check-expect` and `check-within` are part of the language. ; Test-cases include an actual-call (with specific inputs), ; and a specific expected-result. They are *runnable code*! ; ; (comparable to junit's `AssertEquals(Object,Object)` ; and `AssertEquals(double,double,double)`). ; The area (in sq.in.) of a pizza, whose diameter is `diam` (in inches) ; (define (pizza-area diam) ; (-> non-negative-real? non-negative-real?) (* pi (/ diam 2) (/ diam 2))) ; New syntax: `define` for functions. ; Note how it mirrors how the function is called (but params instead of argument-expressions). ; Syntax for define-a-function: ; #| TODO in class |# ;;; EXAMPLE: Together, we'll write: `monogram`: ; Given a first name and last name, return a monogram with initials: (check-expect (monogram "Ian" "Barland") "i.b.") (check-expect (monogram "Jay" "Z") "j.z.") (check-expect (monogram "Tracy" "Lewis-Williams") "t.l.") (check-expect (monogram "50¢" "!&*^") "5.!.") ; Given a first name `f-name` and last name `l-name`, return a monogram with initials: #;(define (monogram f-name l-name) ;(-> non-empty-string? non-empty-string? string?) (string-append (string-downcase (substring f-name 0 1)) "." (string-downcase (substring l-name 0 1)) ; repeated code! ".")) ;; We've repeated the 'substring', 'string-downcase', and appending-to-"." twice! ;; Should our boss ever change our requirements, we'll have to apply our changes to ;; *both* parts of the code (and if we forget, we'll have a bug). ;; Poor design, not having a single point-of-control. ;; ;; Now, let's re-factor `monogram`, to get rid of the repeated code! #| done in class: |# ; Given a first name `f-name` and last name `l-name`, return a monogram with initials: (define (monogram f-name l-name) ;(-> non-empty-string? non-empty-string? string?) (string-append (initialize f-name) (initialize l-name))) ;;; EXAMPLE: Okay, now YOU write: `initialize`: ; Return the first letter of a word downcase'd, followed by ".". (check-expect (initialize "Ian") "i.") (check-expect (initialize "e.e.cummings") "e.") ; Return the initial" for `wrd` (first letter downcased, followed by '.') (define (initialize wrd) ;(-> non-empty-string? non-empty-string?) (string-append (string-downcase (substring wrd 0 1)) ".")) #| One reaction to factoring out `initialize` above is: "Gee Barland, you did a bunch of extra work, for a fairly small amount of duplicate-code-elimination." (a) Duplicate code smells bad; (b) Smaller functions leads to better testing-via-unit-tests; it reduces the need for printf-debugging or stepping with a debugger. (c) The helper-function might even be useful in its own right. |# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;; Bonus full-racket code-teaser ;;;;;;;;;;;;;;;;;;;;;;;;; #| As an example of (c) above, AND a preview of the higher-order function `map` that we'll discuss later, here's another function. (You are NOT expected to be able to sit down and write this ...yet!) The following code uses the full-racket language; copy/paste the contents of the #|...|# below into a new DrRacket window, and select: Languages > Choose Language... > The Racket Language ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |# #| #lang racket ; VERSION 1: (acronymize "Self contained underwater breathing apparatus") (require rackunit) (define (non-empty-string? str) (and (string? str) (>= (string-length str) 1))) (check-equal? (non-empty-string? 17) #f) (check-equal? (non-empty-string? "") #f) (check-equal? (non-empty-string? "hi") #t) ; initialize: non-empty-string -> string ; Return the first letter of a word downcased, following by ".". (define/contract (initialize wrd) (-> non-empty-string? string?) (string-append (string (char-downcase (string-ref wrd 0))) ".")) (check-equal? (initialize "Ke$ha") "k.") (check-equal? (initialize "z") "z.") ; acronymize : string -> string ; Given a multi-word phrase, return its acronym. ; pre-condition: no "empty words" in phrase, caused by multiple spaces in a row. ; (define/contract (acronymize phrase) (-> string? string?) (apply string-append (map initialize (string-split phrase)))) (check-equal? (acronymize "Self contained underwater breathing apparatus") "s.c.u.b.a.") ;;; Again, to be clear: ;;; In this class we will NOT use full-racket, nor worry about regexp's etc. ;;; (We will, though, use `map` in intermediate-student, in a few weeks.) |# #| ;; VERSION 2 -- filter out common words (which are an optional-arg, with common defaults): ; ;(acronymize "The light-amplified stimulated emission of radiation") ;(acronymize "The good ol' United States o' America" '("the" "o'")) (module+ test (require rackunit)) (define (non-empty-string? str) (and (string? str) (>= (string-length str) 1))) (module+ test (check-equal? (non-empty-string? 17) #f) (check-equal? (non-empty-string? "") #f) (check-equal? (non-empty-string? "hi") #t) ) ; initialize: non-empty-string -> string ; Return the first letter of a word downcased, following by ".". (define/contract (initialize wrd) (-> non-empty-string? string?) (string-append (string (char-downcase (string-ref wrd 0))) ".")) (module+ test (check-equal? (initialize "Ke$ha") "k.") (check-equal? (initialize "z") "z.") ) (define COMMON-WORDS (list "the" "and" "of" "or" "and")) ; Return an acroynum for the given `phrase`, not including `words-to-ignore`. ; Hyphenated-words get all sub-words included. ; pre-condition: no "empty words" in phrase, caused by multiple spaces-or-dashes in a row. ; (define/contract (acronymize phrase [words-to-ignore COMMON-WORDS]) (->* (string?) [(listof string?)] string?) (apply string-append (map initialize (filter (λ(str) (not (member str words-to-ignore))) (string-split (string-downcase phrase) #px"[ -]"))))) ; Note that this function has an optional-argument `words-to-ignore` (which affects the contract). (check-equal? (acronymize "Self contained underwater breathing apparatus") "s.c.u.b.a.") (check-equal? (acronymize "The light-amplified stimulated emission of radiation") "l.a.s.e.r.") (check-equal? (acronymize "The good ol' United States o' America" '("the" "o'")) "g.o.u.s.a.") ;;; Again, to be clear: ;;; In this class we will NOT use full-racket, nor worry about regexp's etc. ;;; (We will, though, use `map` in intermediate-student, in a few weeks.) |#