EBoard 07: Conditionals and other stuff

This class will be recorded! Its use is limited to members of the class. Please do not share with others.

Approximate overview

  • Administrative stuff [~10 min]
  • Lab notes [~30 min]
    • Tracing
    • Median
    • IDs
  • Q&A [~10 min]
  • Quiz [~10 min]
  • Lab [~30 min]

Administrative stuff

(when ((close-to-finishing-during-class students conditionals-lab) < 1/2)
  (announce "We will continue this lab tomorrow")
  (note "This won't always happen")
  (re-jigger! syllabus))

Notes and News

  • Happy national pizza day!
  • Evening tutoring will be available 3-5 p.m. Sundays and 8-10 p.m. Sundays through Thursdays in the tutoring channel on the CS team.
    • If evening tutors + mentor sessions are not enough, we do have individual tutors available.
    • Evening tutors like visitors.
  • We have a mailing list in which we announce a variety of things (talks, job opportunities, etc.). Let me know if you’d like to join it.
  • You’d think that after twenty-plus years teaching this course, I’d get the lab lengths right. However, for reasons I don’t quite understand, the time students take in different semesters varies significantly.
  • Sorry about the reading confusion. That’s NOT fixed now. The lists reading is for Friday.
  • Some time between now and tomorrow’s class, I will be DM’ing you a short (potentially strange) statement. I will ask you to read it in class tomorrow. Please do not share it with others in advance.
  • If the mediocre autograders report errors, please do not ignore them.
    • If they seem to be accurate errors, consider fixing them (or writing about why you won’t fix them, depending on the situation) (E.g., in a lab writeup, you can say “We tried this but failed; we’ll talk to Sam.”)
    • If they seem to be inaccurate errors, please let me know.
    • If they make no sense, please talk to me or a mentor.
  • I’m still working on makeup quizzes. Soon; I promise.
  • Sam’s promised story.
    • EC for Mae
  • Warning! I’ll be calling on you randomly today.

Upcoming activities and other token earning things

When appropriate, I will post details to the Announcements channel.

  • Try out Tab for a Cause, https://tab.gladly.io?u=rebelsky, co-created by Kevin Jennison ‘12. “Annoying ads for charity.”
  • Try out the campus Maker Space (well, the Satellite Maker Space) https://makerlab.sites.grinnell.edu/satellite-home/
  • New: History Table: Conversations about Race, inequality, and social justice. Noon, Wednesday (Tomorrow).
  • Scholars’ Convocation, noon, Thursday, 11 February
  • CS Extras, 5pm, Thursday, 11 February, in Events Channel on CS Team. Summer research opportunities in CS.

Upcoming work

I’m not sure if all of these links are correct. Let me know if any are not.

  • Lab writeup from today
  • Reading writeup
    • A different kind of reading.
    • A different kind of reading writeup.
  • We have our first SoLA on Thursday.
    • I’ll update the sample SoLA this evening.
    • Our mentors will review the SoLA in the mentor session. tomorrow evening at 7pm.
    • Thursday’s class is optional; I will answer any questions.
  • Mini-project 2 to be assigned tomorrow.
  • We will have another short (8 min) quiz today.
    • Topic: Tracing conditionals
    • Today’s quiz will not involve programming, but you may want to check your trace, so have DrRacket open.
    • You can also open appropriate Web pages.
      • Yesterday’s lab (your code, your partner’s code)
      • Reading on mental models
      • Reading on conditionals

Attendance

  • Our wonderful mentors will take attendance by looking at the the list of also-wonderful people here.

Tracing conditionals

Rules for tracing conditionals

  • (if TEST CONSEQUENT ALTERNATE): Evaluate TEST.
    • If TEST evaluates to something truish, replace the if expression with CONSEQUENT and continue evaluating the consequent.
    • If TEST evaluates to something false, replace the if expression with ALTERNATE and continue.
    • Note: Violates our normal order of evaluation, but it may be important to do so.
  • (and EXP1 EXP2 ...): First, evaluate EXP1.
    • If the value of EXP1 is false, stop and return false.
    • If the value of EXP1 is truish, evaluate (and EXP2 ...).
    • Note: Violates our normal order of evaluation
    • Special case: (and EXP1) is the value of EXP1.
    • Special case: (and) is true. Because and returns false if any of its parameters is false.
  • (or EXP1 EXP2 ...): First, evaluate EXP1.
    • If the value of EXP1 is false, evaluate (or EXP2 ...).
    • If the value of EXP1 is truish, return the value of EXP1.
    • Note: Violates our normal order of evaluation
    • Special case: (or) is false.

Like all tracing, we take it a step at a time.

(define fun
  (lambda (x)
    (if (integer? x)
        (if (odd? x)
            'oi
            'ei)
        'ov)))

For convenience, let’s write it like this. (Don’t do so in practice.)

(define fun
  (lambda (x)
    (if (integer? x) (if (odd? x) 'oi 'ei) 'ov)
    ))

Why the evaluation order?

(fun 2.4)
; Make sure the parameter is evaluated.  Check.
; Replace the procedure call with the body of the procedure
; while subtituting in the actual argument for the formal parameter. --> (if (integer? 2.4) (if (odd? 2.4) 'oi 'ei) 'ov)
; If we follow the normal order of replacement/tracing, we'd
; evaluate the (odd? 2.4), which gives us a crash.
; Real order: Evaluate the (integer? 2.4) --> (if #f (if (odd? 2.4) 'oi 'ei) 'ov)
; Order: Keep only the alternate because the test return false --> 'ov ```

Real practice

    (fun (fun 4))
    ; Rule: Find the procedure.
    ; Find the parameter for the inner call.  (We need to evalaute
    ; the parameter to the outer call before we apply the outer proc.)
    ; That parameter is 4.
    ; Substitute
--> (fun (if (integer? 4) (if (odd? 4) 'oi 'ei) 'ov))
    ; Evaluate the test (integer? 4)
--> (fun (if #t (if (odd? 4) 'oi 'ei) 'ov))
    ; Replace by consequent
--> (fun (if (odd? 4) 'oi 'ei))
    ; Evaluate test
--> (fun (if #f 'oi 'ei))
    ; Take alternate
--> (fun 'ei)
    ; Substitute (body for proc, arg for param)
--> (if (integer? 'ei) (if (odd? 'ei) 'oi 'ei) 'ov)
    ; Evaluate the test
--> (if #f (if (odd? 'ei) 'oi 'ei) 'ov)
    ; Take the alternate
--> 'ov
    ; We're done

Tracing and

(define poi
  (lambda (val)
    (and (integer? val) (positive? val) (odd? val))))

Why order of evaluation matters.

    (poi 2.4)
    ; Substitute body of procedure, substitute arg for param
--> (and (integer? 2.4) (positive? 2.4) (odd? 2.4))
    ; Suppose we evaluate all of the parameters to and
--> (and #f (positive? 2.4) (odd? 2.4))
--> (and #f #t (odd? 2.4))
    BOOM!

Evaluating that correctly

    (poi 2.4)
    ; Substitute body of procedure, substitute arg for param
--> (and (integer? 2.4) (positive? 2.4) (odd? 2.4))
    ; Evaluate first parameter
--> (and #f (positive? 2.4) (odd? 2.4))
    ; And stop with #f
--> #f

Another example

    (poi -5)
--> (and (integer? -5) (positive? -5) (odd? -5))
--> (and #t (positive? -5) (odd? -5))
--> (and (positive? -5) (odd? -5))
--> (and #f (odd? -5))
--> #f

And another

    (poi 4)
--> 

And another

    (poi 5)
--> 

Finding the median

Option: List all the cases

(define median-of-three-a
  (lambda (x y z)
    (cond
      [(<= x y z)
       y]
      [(<= x z y)
       z]
      [(<= y x z)
       x]
      [(<= y z x)
       z]
      [(<= z x y)
       x]
      [(<= z y x)
       y]
      [else
       (error "Math makes no sense")])))

Note: If you use < rather than <=, you may get some strange issues if two values are the same.

Option: Decision tree

(define median-of-three-b
  (lambda (x y z)
    (if (<= x y)
        (if (<= y z)
            y
            (if (<= x z)
                z
                x))
        (if (<= x z)
            x
            (if (<= y z)
                y
                z)))))

Option: Math!

Idea: “Chop off the smallest and largest.”

(define median-of-three-c
  (lambda (x y z)
    (- (+ x y z)
       (min x y z)
       (max x y z))))

Option: Some weird hybrid

Done live!

(define median-of-three-d
  (lambda (x y z)
    (if (<= x y)
        (if (<= y z)
            y
            (max x z))
        (if (<= x z)
            x
            (in y z)))))

Question: Which do you prefer? Why?

  • a: Exhaustive list or c: Streamlined
  • Agreed.

Which of a and b is likely to be faster / more efficient?

  • b: Fewer comparisons! a does as many as 12 comparison, b does no more than 3.

Valid ids

I did not write this problem, so I’m not completely certain what my colleague was thinking. But I’d like to talk about it and the possible issues you might encounter.

In a particular company, employees are given IDs that consist of a string of 6 digits, e.g., “419109”.

Write a procedure, (employee-id? str), that takes a string as input and determines whether the string is a valid employee id. You need not check that str is a string.

Step zero: Document

(You’re not expected to do that yet, but you will be soon.)

;;; (employee-id? str) -> boolean?
;;;   str : string?
;;; Determine if str is a valid id, in this case, a length-six
;;; string all of whose characters are digits.

Step one: Consider behavior

What are some inputs you might try and what answers do you expect?

  • "123456" -> #t [“normal” case]
  • "419109" -> #t [“normal” case]
  • "1" -> #f [too few digits]
  • "000659" -> #t [leading zeros]
  • "65432x" -> #f [right length, non-digit]

Step two: Implement

Spend a few minutes writing the procedure (or thinking about how you would write the procedure).

Hint: string->number may help.

Sam’s first solution.

(define employee-id?
  (lambda (str)
      (and (string->number str)
           (= (string-length str) 6))))
  • string->number returns false if str contains non-digits.
  • the second test checks if it has the right number of letters.

Step three: Reconsider behavior

What are some stranger inputs that might cause problems? (“Edge cases” “Corner cases”)

Black box: Without knowing the implementation.

White box: If we do know the implementation.

Inputs / expectations / explanations

  • Not a string. We’ll fix that anyway.
(define employee-id?
  (lambda (str)
    (and (string? str)
         (string->number str)
         (= (string-length str) 6))))
  • I’m worried about string->number and string-length
  • If we wrote it differently, we might get a number (a truish value) rather than #t or #f.
  • Note: (string->number str) returns false if it cannot convert the string to a number. (This can be a good strategy for procedure design; instead of crashing, return false.)
  • “250/50” or “1.0000” should both return false
  • But it returns true for “250/50”
  • And it returns true for “1.0000”
  • It returns true for “100000”, but it should.

How might you fix that?

What other potential problems are there?

You don’t have to deal with them in the lab, but you should think.

Q&A

Please try to ask questions on the Q&A channel and leave this section for (a) things I am unclear about in that channel and (b) things that came up while you were listening to me this a.m.

Why isn’t the schedule fixed?

Um. It will be.

Can you tell me more about the string->number procedure.

> (string->number "000111")
111

But we are checking the string length of the original number.

Lab

Same partners! Switch who starts.

Please make sure to title your group correctly.