;; CMPU101, Spring 2013 ;; Lecture #5 ;; Read pages 42-47 of posted readings for discussion of if and ;; cond special forms. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Making decisions--IF special form: (number 8) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; One of the most critical operations done by computer programs ;; is decision making. For example, a program may need to test ;; if a number has a particular value or is within some range of ;; values. ;; ;; The most basic decision-maker is the IF special form. This ;; special form is best suited to the situation where there are ;; only 2 possible outcomes, #t or #f. ;; ;; The general shape of an IF special form is: ;; ;; (if questionExpression ;; questionTrueReturn ;; questionFalseReturn) ;; ;; The keyword IF is followed by three arguments, separated by ;; white space (that includes tabs, line breaks, etc). Naturally, ;; the entire expression is enclosed in parentheses as are all ;; special forms. ;; ;; The if special form is evaluated as follows: ;; ;; - The questionExpression is always evaluated. ;; ;; - If the questionExpression is true, the ;; questionTrueResult is evaluated and the ;; questionFalseResult is not evaluated. ;; ;; - Else, if the questionExpression is false, ;; the questionTrueResult is not evaluated and ;; the questionFalseResult is evaluated. ;; ;; - The questionTrueResult and questionFalseResult ;; are never both evaluated. ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Here is an example function that could use an if special form: ;; ;; Ex1: Define a function to return the inverse of its ;; input for non-zero integers or 0 if input is 0. ;; CONTRACT: (inverse integer) -> number ;; HEADER: (define inverse (lambda (int) ...)) ;; PURPOSE: return inverse of int or 0 ;; PRE-FUNCTION TESTS: (check-expect (inverse 0) 0) (check-expect (inverse 1) 1) (check-expect (inverse -11) -1/11) ;; FUNCTION DEFINITION: (define inverse (lambda (int) ; check if int is not 0 (if (not (= int 0)) ; if int is not 0, return 1/int (/ 1 int) ; otherwise, int=0, so return 0 and avoid dividing by 0 ; (an arithmetic error) 0))) ;; POST-FUNCTION PRINTFs: (printf "(inverse 50) => ~a~%" (inverse 50)) (printf "(inverse 0) => ~a~%" (inverse 0)) (printf "(inverse 1) => ~a~%" (inverse 1)) ;; In the event the result of the first expression is true, then ;; the second expression is evaluated (the true part); otherwise ;; the third expression is evaluated (the false part). Whichever ;; expression is evaluated, it is also the result (output) of the ;; entire if expression. ;;-------------- ;; Ex: Write a function called odd-or-even that consumes a natural number ;; and returns the string "odd" if the number is odd and "even" if the ;; number is even. ;; Contract: (odd-or-even nat-num) -> string ;; Header: (define odd-or-even (lambda (n) ;; Purpose: Return string representation of n, either "odd" or "even". ;; Constants used in function: (define ODD-txt "odd") (define EVN-txt "even") ;; Pre-function tests: (check-expect (odd-or-even 73) "odd") (check-expect (odd-or-even 42) "even") ;; Function definition: (define odd-or-even (lambda (n) (if (= 1 (remainder n 2)) ODD-txt EVN-txt))) ;; Post-function printfs: (printf "(odd-or-even 73) => ~a~%" (odd-or-even 73)) (printf "(odd-or-even 2) => ~a~%" (odd-or-even 2)) ;;-------------- ;; Ex: Write a function called letter-grade that takes a natural number ;; less than or equal to 100 as input and produces the string ;; "A" if the number is greater than or equal to 85, "B" if the number ;; is less than 85 but greater than or equal to 72, "C" if the ;; number is less than 72 but greater than or equal to 60, "D" if the ;; number is less than 60 but greater than or equal to 50, and "F" if the ;; number is less than 50. Use "nested" or "cascading ifs". ;Constants to represent score boundaries and letter grades (define HIs 85) ;\ (define MEs 72) ; Raw score (define AVs 60) ; boundaries (define JPs 50) ;/ (define HIg "A") ;\ (define MEg "B") ; (define AVg "C") ; Letter grades (define JPg "D") ; (define Fg "F") ;/ ;Contract: (letter-grade nat-num) -> string ;Header: (define letter-grade (lambda (score) ...)) ;Purpose: Return letter grade based on score. ;Pre-function tests: (check-expect (letter-grade 99) "A") (check-expect (letter-grade 84) "B") (check-expect (letter-grade 60) "C") (check-expect (letter-grade 50) "D") (check-expect (letter-grade 5) "F") ;Function definition: (define letter-grade (lambda (score) ; highest (if (>= score HIs) HIg ; meets all criteria (if (>= score MEs) MEg ; average (if (>= score AVs) AVg ; just passing (if (>= score JPs) JPg ; failing Fg)))))) ;Post-function printfs: (printf "(letter-grade 99) => ~a~%" (letter-grade 99) ) (printf "(letter-grade 89) => ~a~%" (letter-grade 89) ) ; ; One of the most common errors beginner programmers make is to use an if ; statement when only a single boolean expression is needed. ; ; Ex6: Write a function called may-drive? that consumes a number representing ; the age of a person and returning true if the age is >= 16 and false ; otherwise. ;Contract: (may-drive? nat-num) -> boolean ;Header: (define may-drive? (lambda (age) ...) ;Purpose: Return true if age >= 16 and false otherwise ;Constant used in following 2 functions: (define LEGAL-AGE 16) ;Pre-function tests: (check-expect (may-drive? 15) #f) (check-expect (may-drive? 55) #t) (check-expect (may-drive? 16) #t) ;Function definition: (define may-drive? ; version 1 using if (lambda (age) (if (>= age LEGAL-AGE) #t #f))) ;Post-function tests: (printf "(may-drive? 15) => ~a~%" (may-drive? 15)) (printf "(may-drive? 16) => ~a~%" (may-drive? 16)) (printf "(may-drive? 85) => ~a~%" (may-drive? 85)) ;Contract: (may-drive-v2? nat-num) -> boolean ;Header: (define may-drive-v2? (lambda (age) ...) ;Purpose: Return true if age >= 16 and false otherwise ;Pre-function tests: (check-expect (may-drive-v2? 15) #f) (check-expect (may-drive-v2? 55) #t) (check-expect (may-drive-v2? 16) #t) ;Function definition: (define may-drive-v2? ; version 2 using only a boolean expression (lambda (age) (>= age LEGAL-AGE))); this expression returns either #t or #f; no if needed ;Post-function tests: (printf "(may-drive-v2? 15) => ~a~%" (may-drive-v2? 15)) (printf "(may-drive-v2? 16) => ~a~%" (may-drive-v2? 16)) (printf "(may-drive-v2? 85) => ~a~%" (may-drive-v2? 85)) ; Nested Conditionals: ; ; You can write one decision statement inside another, e.g., an if inside ; a cond clause, a cond inside a cond clause, an if inside an if, or a cond ; inside an if. These are called nested ifs and nested conds. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The letter-grade function requires the "cascading" of if expressions. ;; Cascading ifs can be hard to read and understand. ;; ;; For decisions that have more than 2 possibilities, it is more ;; convenient to use another special form decision maker: COND ;; ;; The general shape of a COND special form is: ;; ;; (cond ;; [question1 answer1] ;; [question2 answer2] ;; ... ;; [else answerN]) ;; ;; where question1...questionN are predicates (inside parens) ;; that each evaluate to a boolean, and answer1...answerN evaluate ;; to the return value of the cond statement. The [question answer] ;; pairs are called CLAUSES. ;; ;; ELSE is a keyword that evaluates to #t and it can be replaced by ;; #t in the final clause. ;; ;; The cond statement evaluates in the following way: if question1 ;; is true, evaluate answer1; otherwise if question1 is false, skip ;; answer1 to evaluate question2. If question2 is true, evaluate ;; answer2; otherwise if question2 is false, skip answer2 to evaluate ;; question3... ;; ;; If none of the questions evaluates to true there is an error. That's ;; why the else is given at the end. The else answer is always evaluated ;; when none of the clauses that come before are evaluated. ;; ;; Only one answer is ever evaluated in a cond, just like the if. ;;---------------- ;; ;; Ex: Rewrite the letter-grade function written above so that ;; it uses a cond expression instead of a series of if state- ;; ments. ;Contract: (letter-grade-cond nat-num) -> string ;Header: (define letter-grade-cond (lambda (score) ...)) ;Purpose: Return letter grade based on score. ;Constants to represent score boundaries and letter grades ;(DEFINED ABOVE) ;(define HIs 85) ;\ ;(define MEs 72) ; Raw score ;(define AVs 60) ; boundaries ;(define JPs 50) ;/ ; ;(define HIg "A") ;\ ;(define MEg "B") ; ;(define AVg "C") ; Letter grades ;(define JPg "D") ; ;(define Fg "F") ;/ ;Pre-function tests: (check-expect (letter-grade-cond 99) "A") (check-expect (letter-grade-cond 84) "B") (check-expect (letter-grade-cond 60) "C") (check-expect (letter-grade-cond 50) "D") (check-expect (letter-grade-cond 5) "F") ;Function definition: (define letter-grade-cond (lambda (score) (cond ;; highest grade [(>= score HIs) HIg] ;; next-to highest grade [(>= score MEs) MEg] ;; average grade [(>= score AVs) AVg] ;; sub-average grade [(>= score JPs) JPg] ;; failing grade [else Fg]))) ;Post-function printfs: (printf "(letter-grade-cond 5) => ~a~%" (letter-grade-cond 5)) (printf "(letter-grade-cond 65) => ~a~%" (letter-grade-cond 65)) (printf "(letter-grade-cond 75) => ~a~%" (letter-grade-cond 75)) ;; ;;---------------- ;; ;; Ex: Develop a function classify that tells what type of data its ;; input is by returning one of the strings "image", "string", ;; "number", "boolean", or "other", as appropriate. ;Constants used in function: (define Ntype "number") (define Otype "other") (define Stype "string") (define Btype "boolean") (define Itype "image") ;Contract: (classify any-valid-racket-type) -> string ;Header: (define classify (lambda (any) ...)) ;Purpose: Return the type of any as a string or "other" ;Pre-function tests: (check-expect (classify 73) "number") (check-expect (classify 'abc) "other") (check-expect (classify "abc") "string") (check-expect (classify #f) "boolean") (check-expect (classify (circle 1 'solid 'red)) "image") ;Function definition: (define classify (lambda (any) (cond ; any is number [(number? any) Ntype] ; any is image [(image? any) Itype] ; any is boolean [(boolean? any) Btype] ; any is string [(string? any) Stype] ; any is any other valid racket type [else Otype]))) ; ; If you want to write more than one statement inside each branch of ; an if or within a cond clause, you need to use a (begin ...) around ; the rest of the statements in the return portion of the cond. ;