;; 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-intermediate-lambda-reader.ss" "lang")((modname lect03b) (read-case-sensitive #t) (teachpacks ((lib "universe.ss" "teachpack" "2htdp") (lib "image.ss" "teachpack" "2htdp"))) (htdp-settings #(#t constructor repeating-decimal #f #t none #f ((lib "universe.ss" "teachpack" "2htdp") (lib "image.ss" "teachpack" "2htdp"))))) ;;;;; lect03b.rkt ;;; Code from last time: ;;; Since we're handling data that can be integer OR character, ;;; we'll use "cond", which is like if-else-if: #;(define (func-for-ranks a-r) (cond [(integer? a-r) ...] [(symbol? a-r) ...])) ;;; In bridge: ;;; you assign "bridge values" to card-ranks: ;;; most cards are 0, but jack is 1, queen is 2, king is 3, ace is 4. ;;; test cases (write 'em first!) (check-expect (rank->pts 5) 0) (check-expect (rank->pts 'queen) 2) ; crank-value : c-rank -> int ; compute the bridge-playing value for a given c-rank. (define (rank->pts a-r) (cond [(integer? a-r) 0] [(symbol? a-r) (cond [(symbol=? a-r 'jack) 1] [(symbol=? a-r 'queen) 2] [(symbol=? a-r 'king) 3] [(symbol=? a-r 'ace) 4])])) (check-expect (rank->string 5) "5") (check-expect (rank->string 10) "10") (check-expect (rank->string 'queen) "Q") ; crank->string : c-rank -> string ; (define (rank->string a-cr) (cond [(integer? a-cr) (number->string a-cr)] [(symbol? a-cr) (string (char-upcase (string-ref (symbol->string a-cr) 0)))])) ; A rank is either: ; - an integer (2..10) ; or - a symbol ('jack, 'queen, 'king, 'ace) ; OPTIONAL: ; Btw, we can write: ; rank? ANY -> boolean ; (define (rank? any) (or (and (integer? any) (<= 2 any 10)) (symbol? any))) ; ; The design recipe: ; http://htdp.org/2003-09-26/Book/curriculum-Z-H-7.html#node_sec_4.4 ; --- per (abstract) data type: ; - data def'n ; - examples of data ; - template (case analysis) ; --- per function: ; - contract, purpose, header ; - test-cases (check-expect) ; - complete function-body ; - case analysis (cond-questions, 1 per data variant) ; - answer for each cases ; - run the tests (trivial) ;;;;;;;;;;;;;;;;; Next stage: define-struct (product type, compound) ; ; Example: (define-struct card (r s)) ; r : rank ; s : symbol (define 4s (make-card 4 'spades)) (define qd (make-card 'queen 'diamonds)) #| The corresponding Java: class Card { Rank r; String s; } ... new Card( 4, "hearts" ) ... |# ; When I define-struct, racket creates four functions for me: ; make-card -- constructor ; card-r -- accessor ; card-s -- accessor ; card? -- predicate ; have-a-heart : rank -> card ; Return a card of the given rank, in hearts. ; (define (have-a-heart a-cr) (make-card a-cr 'hearts)) (check-expect (have-a-heart 3) (make-card 3 'hearts)) (check-expect (have-a-heart 'jack) (make-card 'jack 'hearts)) #| In Java: Card haveAHeart( Rank aCr ) { return new Card(aCr, "hearts"); } |# ; Template for any card-processing function: ; #;(define (func-for-card c) ...(card-r c) ; type: rank; rank->pts ...(card-s c)) ; type: symbol; useful functions: symbol=? ; card->pts : card -> number (define (card->pts c) (rank->pts (card-r c))) (check-expect (card->pts qd) 2) (check-expect (card->pts 4s) 0) ; turn-to-spades: Given a card, return a new card of the same rank, and suit 'spades. ; ; turn-to-spades : card -> card (define (turn-to-spades a-card) ;...(card-r a-card)... ; type: rank ;...(card-s a-card)... ; type: symbol (make-card (card-r a-card) 'spades) ) (check-expect (turn-to-spades (make-card 7 'hearts)) (make-card 7 'spades)) (check-expect (turn-to-spades (make-card 7 'spades)) (make-card 7 'spades)) ;;;; What about: ;;;; A structure that contains other structures? ;;;; A hand is (say) two cards, plus whether or not ;;;; those cards has been revealed to all: ;;;; (define-struct hand (c1 c2 revealed?)) ; c1 : card ; c2 : card ; revealed? : boolean ;;; examples of the data: (define h1 (make-hand 4s qd true)) (make-hand 4s qd (not (symbol=? 'hello 'bye))) (make-hand (make-card 4 'spades) (make-card 'queen 'diamonds) true) (define h2 (make-hand (make-card 'queen 'diamonds) (make-card 4 'spades) false)) ; hand-revealed? : hand -> boolean ; hand-c1 : hand -> card ;; func-for-hand: hand -> ...??... ;; #;(define (func-for-hand h) ... (hand-c1 h) ; card turn-to-spades card->string ... (hand-c2 h) ; card turn-to-spades ... (hand-revealed? h)) ; boolean cond, if, or, and, not ;;; Step: inventory the fields and what you can do with them ;;; HOWEVER: do NOT call any further selectors on cards: ;;; DOn't call card-suit, don't call card-rank ... call a helper instead. (check-expect (hand->string h1) "4S,QD") (check-expect (hand->string h2) "face-down") (define (card->string crd) (string-append (rank->string (card-r crd)) (symbol-initial-string (card-s crd)))) (define (symbol-initial-string sym) (string (char-upcase (string-ref (symbol->string sym) 0)))) ;; hand->string : hand -> string ;; (define (hand->string h) (if (hand-revealed? h) (string-append (card->string (hand-c1 h)) "," (card->string (hand-c2 h))) "face-down")) #| In Java: class Hand { public String toString( /* Hand this, */ ) { return this.c1.toString() + "," + this.c2.toString() + "," + (this.revealed ? "revealed" : "hidden"); } } |# (check-expect (hand->string (make-hand (make-card 4 'spades) (make-card 'queen 'diamonds) true)) "4S,QD") (check-expect (hand->string (make-hand (make-card 4 'spades) (make-card 'queen 'diamonds) false)) "face-down") ; design recipe step: inventory ; card>? ; card<=? ; Here's a union type: ; A player-status is either: ; - a hand, or ; - an int (#minutes until next chance to play) ; a TEMPLATE for a function which handles a player-status. ; #;(define (handle-player-status ps) (cond [(hand? ps) ...(hand-c1 ps)...(hand-c2 ps)...(hand-revealed? ps)] [(integer? ps) ...])) ; Consider the function: ; next-winning: player-status -> non-negative real? ; Any compulsive gambler can tell you: ; - if good hand, 2.0; ; - if bad hand, 0.1; ; - if waiting: 1.9; ; a TEMPLATE for a function which handles a player-status. ; #| (define (next-winning ps) (cond [(hand? ps) (if (>= (+ (card->pts (hand-c1 ps)) (card->points (hand-c2 ps))) 4) 2.0 0.1)] [(integer? ps) 1.9])) ; Not bad, but started to get cumbersome; can we break down the problem ; a bit differently, to keep it simple? |# (define (next-winning ps) (cond [(hand? ps) (if (>= (hand->pts ps) 4) 2.0 0.1)] [(integer? ps) 1.9])) (define (hand->pts h) (+ (card->pts (hand-c1 h)) (card->pts (hand-c2 h)))) ;;;;;;; ; Data Definition: ; list-of-number is: ; (cons number list-of-number) ; "constructed list" ; or: empty ; Examples of data: (cons 3 (cons 2 (cons 4 empty))) ;(3 2 4) (cons 2 (cons 4 empty)) ; (2 4) (cons 4 empty) ; (4) empty ; () ; empty is a built-in constant ; cons is a built-in structure with two fields: first, rest ;(define-struct cons (first rest)) (define (sum lon) (cond [(cons? lon) (+ (first lon) (sum (rest lon)))] [(empty? lon) 0])) (check-expect (sum (cons 3 (cons 2 (cons 4 empty)))) 9) (check-expect (sum (cons 2 (cons 4 empty))) 6) (check-expect (sum (cons 4 empty)) 4) (check-expect (sum empty) 0) (check-expect (symbol-initial-string 'hello) "H") (check-expect (card->string (make-card 4 'spades)) "4S") (check-expect (card->string (make-card 'queen 'diamonds)) "QD")