; CS 111 - Week 4 Lecture 1 - 2016-09-13
; Sharon Tuttle
; last modified: 2016-09-13 (cleaned up a bit after class)

(require 2htdp/image)
(require 2htdp/universe)

;--------
; BSL Racket has more than one expression
;     for branching --
; we're introducing one of them,
;     the cond expression,
;     today;

;--------
; cond - short for conditional!
; syntax:
;
; (cond
;     [bool-expr1 expr1]
;     [bool-expr2 expr2]
;     ...
;     [bool-expr-n expr-n]
;     [else expr-else]
; )

; semantics:
; *   if bool-expr1 is true, then the value
;     of the whole cond expression is expr-1
; *   BUT if bool-expr1 is false, then DrRacket
;     sees if bool-expr2 is true, and if so,
;     the value of the whole cond expression is
;     expr2
; ...
; *   IF it reaches the else, then expr-else
;     is the value of this cond expression

;--------
; AFTER you click run,
; NOTICE the dark highlighting indicating
;    expressions that were NOT
;    evaluated/executed
;    in the following cond expression;
;
; (if the boolean expression starting a cond expression clause
;     is #false, its result expression is never evaluated;
; once a #true boolean expression starting a cond expression
;     clause is reached, none of the cond expression clauses
;     after that are evaluated...)

(cond
    [#false 13]
    [(< 1 3) "oink"]
    [(< 10 20) "moo"]
    [else "red"]
)

; let's write a function that uses a cond expression!

;--------
; signature: greet: string number -> string
; purpose: expects a name and the hour of the
;    day expressed in 24-hour time, [0, 23],
;    and returns a greeting appropriate to
;    the time of day:
;    [0, 12) - morning
;    [12, 18) - afternoon
;    [18, 23] - evening

;(define (greet name time)
;    ...
;)

;--------
; OY!! when you have MULTIPLE categories
;    of data,
; you need at LEAST one test PER category;
; AND -- if that data is INTERVAL data,
;    (it can be expressed in interval form,
;    like morning-time is [0, 12)),
; you need to ALSO test so-called BOUNDARIES
;    of each interval,
; AND -- if you are checking for errors/bad data,
;    TRY to test for that if you can...!


(check-expect (greet "Calvin" 1)
              "Good morning, Calvin!")
(check-expect (greet "Mary" 13)
              "Good afternoon, Mary!")
(check-expect (greet "Steve" 20)
              "Good evening, Steve!")
(check-expect (greet "Molly" 0)
              "Good morning, Molly!")
(check-expect (greet "Sue" 12)
              "Good afternoon, Sue!")
(check-expect (greet "Jimmy" 18)
              "Good evening, Jimmy!")
(check-expect (greet "Sharon" 23)
              "Good evening, Sharon!")

;--------
; ASIDE 1:
;    want your function to stop with an error message?
;    function error can do that:
;
;    (error "desired error message")

;--------
; ASIDE 2:
;    want to write a *test* that checks if a call causes
;        an error without actually having it END your
;        Racket run, function check-error can do that:
;
;    (check-error expr-to-test
;                 "error message that should result")
;

(check-error (greet "Jackie" -1)
             "Time too small - must be in [0, 23]")
(check-error (greet "Bob" 24)
             "Time too big - must be in [0, 23]")
(check-error (greet "Barack" 0.5)
             "Time must be an integer in [0, 23]")

;--------
; NEW DESIGN RECIPE STEP!
; IF your data suggests a TEMPLATE for your body,
;     put in that template NOW, after writing
;     your check-expressions/tests

; for interval data, you often need a cond
;    expression, often with at least
;    the same number of clauses as you
;    have intervals
;    (and sometimes more for error-checking)

;(define (greet name time)
;    (cond
;        [... ...]
;        [... ...]
;        [... ...]
;    )
;)

;--------
; and I might add more for my error checking...

;(define (greet name time)
;    (cond
;        [... ...]
;        [... ...]
;        [... ...]
;        [... ...]
;        [... ...]
;        [... ...]
;    )
;)

;--------
; and then fill in each clause, handling each case
;   one at a time (divide and conquer!!)
;
; (and we found out in class that the ORDER you put the
; clauses may affect how you write the boolean expression
; for a clause...
; *  ...because you only REACH a clause if the boolean expressions
;    of ALL the preceding clauses are #false!)

(define (greet name time)
    (cond
        [(not (integer? time))
             (error "Time must be an integer in [0, 23]")]
        [(< time 0)
             (error "Time too small - must be in [0, 23]")]
        [(< time 12) (string-append
                         "Good morning, "
                         name
                         "!")]
        [(< time 18) (string-append
                         "Good afternoon, "
                         name
                         "!")]
        [(<= time 23) (string-append
                          "Good evening, "
                          name
                          "!")]
        [else (error "Time too big - must be in [0, 23]")]
    )
)

(greet "Calvin" 1)
(greet "Mary" 13)
(greet "Steve" 20)

; next: more examples of functions involving cond expressions