;; 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 lect02a) (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"))))) #| The Law of Scheme: Given an expression, evaluate it to get a value. An Expression is one of: And to evaluate it, you: - value 23, "hi", false, #\A, ... - just use that value (duh!) - identifier x, char->integer - look up reulst of previous `define` - function-application: - Eval exrp0..exprn (expr0 expr1 ... exprn) to get val0, val1, ..., valn (val0 had better be a func!) e.g. (+ 2 (* 3 4) 5); (a) if val0 is a built-in primitive, expr1 is `7` call that with val1...valn, expr2 is `(* 3 4)` and that's your answer. expr3 is `5` (b) if val0 is a user-defined function, expr0 is the abstract func `addition` take the body of the func, subst the params with val1,...,valn Then evaluate *that* for your answer. - Special Forms (with their own syntax, and rules-of-evaluation): define -- don't eval the first "argument" and,or -- short-circuit if,cond -- short-circuit: only eval the 2nd *or* 3rd arg, not both define-struct -- stay tuned... |# ; Introducting `if`, and talking about why it's a special form: ; Syntax: ; (if ) ; Semantics: ; Evaluate to get . ; [In the student-languages: had better be a boolean!] ; If is #f, then eval and return that; ; otherwise eval and return that result. ; ; Why does `if` have to be special? ; Because we really *don't* want it to ; eval *both* of and , ; which is what the normal law-of-scheme does. ; ; Here's an example of we'd hate the normal law-of-scheme: ; bat-avg: int, int -> real ; Return the batting average, ; or 1.000 at the start of the season. ; (define (bat-avg hits times-at-bat) (if (= times-at-bat 0) 1 (/ hits times-at-bat))) (bat-avg 0 0) (bat-avg 0 203) ; Btw: compare `if` to: ; - Java/etc's ternary operator: ; return (timesAtBat==0) ? 1 : hits/timesAtBat; ; - Excel's `if`: =if(A3=0,1,B3/A3) ; Similarly, `define` has to be a special form. ; (Why -- what would happen if define were a normal function, ; using the law-of-scheme?) ; Discuss: `and` is a special form. ; Can you show me an expression to convince me it's not a regular function? ; Does `and` *have* to be a special form? If not, why is it one? ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Union types: ;;; How to handle a data-type that can be of type A OR B? ;;; (that is: since types are sets-of-values: ;;; how to handle any value from the set (union A B)? ) ;;; Example: the rank of a playing-card. ;;; (We'll deal with the suit tomorrow.) ; A card-rank is either: ; - an integer ; or - a character ; Examples of the data, for card-rank: 5 #\Q ;;; Writing code to handle a union type: ;; I want a function to help my bridge-playing freind: ;; you assign 'points' to each card; 2-10 are worth zero ;; but J,Q,K,A are worth 1,2,3,4 resp. ;; A stub function: ; rank-val: card-rank -> integer #;(define (rank-val cr) 17) ;;; test cases (write 'em first!) (check-expect (rank-val 5) 0) (check-expect (rank-val #\Q) 2) ;;; Since we're handling data that can be integer OR character, ;;; we'll use "cond", which is like if-else-if: #;(define (rank-val cr) (cond [(integer? cr) ...] [(character? cr) ...])) ;;; Depending on how paranoid we are, we can add one extra branch ;;; for protection: #;(define (rank-val cr) (cond [(integer? cr) ...] [(character? cr) ...] [else ...])) ;;; Aside: the syntax of `cond`: #;(cond [q1 a1] [q2 a2] ... [qn an]) ;;; (Think q,a for question,answer.) ;;; Each of q1..qn is any expression (presumably evals to a boolean), ;;; or it's the word `else` which is equivalent to `#t`. ;;; Each of a1...an are any expression. ;;; ;;; The semantics of cond: ;;; Evaluate q1; if it's true then return result of eval'ing a1; otherwise ;;; Evaluate q2; if it's true then return result of eval'ing q2; otherwise ;;; ... ;;; Evaluate qn; if it's true then return result of eval'ing qn; otherwise ;;; raise an error (in student-languages -- full lang returns (void)). ;;; q1...qn are either ;;; Okay, let's get back to writing rank-val: #;(define (rank-val cr) (cond [(integer? cr) ...] [(character? cr) ...])) ; We fill in each `...`. ; The first is pretty easy. ; The second will be ... a whole 'nother cond. ; rank-val: card-rank -> integer ; (define (rank-val cr) (cond [(integer? cr) 0] [(char? cr) (cond [(char=? cr #\J) 1] [(char=? cr #\Q) 2] [(char=? cr #\K) 3] [(char=? cr #\A) 4])])) ; A few questions you probably have: ; ; Could we collapse the nested conds? ; Yes, but I don't: ; The outer cond is there for a specific reason: ; it follows from the fact that *any* function handling a card-rank ; needs to ask those questions. ; The inner cond is doing work specific to this function. ; ; Could we have used an `if` instead of the outer `cond`? ; Yes, but again we use the `cond` as a signal to other programmers ; that we're doing a *case analysis* on the *type* of the input.