Skip to main content

CSC 151 2019S, Class 23: Discuss exam 2

Overview

  • Preliminaries
    • Notes and news
    • Upcoming work
    • Extra credit
    • Questions
  • Common problems
  • Improving style
  • Questions
  • Notes on recursion

Preliminaries

News / Etc.

  • Welcome back from break! And happy April Fool’s day!
    • My CSC 207 exams took approximately twice as long to grade as I had anticipated, so I am still behind on everything.
  • Sit alphabetically today (cards are at workstations to help you figure out where to sit)
  • Mentor sessions Wednesday 8-9 p.m., Thursday 8-9 p.m., Sunday 5-6 p.m.
  • Exam 2 returned via email. Notes also distributed via email.
  • Quizzes returned
  • Do you want the traditional “Sam wears a costume on April Fools’ Day” jokes?

Upcoming work

  • Next homework to be assigned on Wednesday. Topic: Recursion.
  • Friday’s quiz: Recursion
  • Reading for Wednesday: See the schedule
  • No lab writeup!
  • No flash cards this week.

Extra Credit

I would certainly appreciate suggestions of other extra credit activities (preferably via email).

Extra credit (Academic/Artistic)

  • Convo Thursday (sorry, I don’t know details yet)
  • Foreign service alum presentation: HSSC S3325, 7:30 tonight.
  • Talk on future of agriculture, 4:15, somewhere in the Husk (HSSC).

Extra credit (Peer)

  • Grinnell Singers, Sunday the 7th at ???
  • Women’s golf, Saturday/Sunday somewhere in St. Louis
  • Track and Field, Saturday at Cornell

Extra credit (Wellness)

Extra credit (Wellness, Regular)

  • 30 Minutes of Mindfulness at SHACS every Monday 4:15-4:45
  • Any organized exercise. (See previous eboards for a list.)
  • 60 minutes of some solitary self-care activities that are unrelated to academics or work. Your email reflection must explain how the activity contributed to your wellness.
  • 60 minutes of some shared self-care activity with friends. Your email reflection must explain how the activity contributed to your wellness.

Extra credit (Misc)

  • Wednesday the 10 at 4pm on Mac Field: Giant Laurel Leaf. (Free t-shirt!)
  • Scarlet and Give Back Day next Wednesday/Thursday (I think). If you don’t have money to donate, let me know and I will give you $5 to donate.

Other good things

Questions

Not questions about the exam. Those come later.

Common problems

  • Too many of you did well. (Not a problem, that’s a good thing.)
  • There are still some issues on postconditions.
  • Some of you need to work on your style (a lot)
  • At this point, you should be avoiding repetitious code using helper procedures or lets or …
  • Not anonymous
  • Use YYYY-MM-DD

Some repetition

Example one

(list-ref (list-ref thing1 0) 1)
(list-ref (list-ref thing2 0) 1)
(list-ref (list-ref thing3 0) 1)
(list-ref (list-ref thing4 0) 1)
(list-ref (list-ref thing5 0) 1)


(define uh ; use helper
  (lambda (thing)
    (list-ref (list-ref thing 0) 1)))

(uh thing1)
(uh thing2)
(uh thing3)
(uh thing4)
(uh thing5)

Note: “uh” is funny, but not a particularly good name.

(regexp-match "<tag1>[^<]*</tag1>" (xml->string xml))
(regexp-match "<tag2>[^<]*</tag2>" (xml->string xml))
(regexp-match "<tag3>[^<]*</tag3>" (xml->string xml))
(regexp-match "<tag4>[^<]*</tag4>" (xml->string xml))
(regexp-match "<tag5>[^<]*</tag5>" (xml->string xml))

Let’s write a helper procedure

(define extract-tag
  (lambda (pattern xml)
    (regexp-match pattern (xml->string xml))))
(extract-tag "<tag1>[^<]*</tag1>")
(extract-tag "<tag2>[^<]*</tag2>")
(extract-tag "<tag3>[^<]*</tag3>")
(extract-tag "<tag4>[^<]*</tag4>")
(extract-tag "<tag5>[^<]*</tag5>")

Here’s a better version

(define extract-tag
  (lambda (tag xml)
    (regexp-match (string-append "<" tag ">[^<]*</" tag ">")
                  (xml->string xml))))
(extract-tag "tag1")
(extract-tag "tag2")
(extract-tag "tag3")
(extract-tag "tag4")
(extract-tag "tag5")

It probably works, but we might be able to do even better with a map or for-each, e.g.,

(map extract-tag (list "tag1" "tag2" "tag3" "tag4" "tag5"))

Note: We are calling xml->string five times. It’s an expensive procedure. We should avoid that.

We could write a let statement instead.

(define extract-tag
  (lambda (tag xmlstr)
    (regexp-match (string-append "<" tag ">[^<]*</" tag ">")
                  xmlstr)))
(let ([xmlstr (xml->string xml)])
  (map (lambda (tag) (extract-tag tag xmlstr))
       (list "tag1" "tag2" "tag3" "tag4" "tag5")))

Same number of lines, but more general and more efficient.

Moral: To avoid repetition,

  • Use helper procedures
  • Use let
  • Use map

Postconditions

Goal: To restate the purpose more formally, thereby avoiding ambiguity.

  • Scheme and Math are good ways to avoid ambiguity.
  • If I can write a procedure that meets your postconditions, but does not achieve the implicit goal, your postconditions don’t suffice.
  • “random-story generates a funny story”. “Sam, while wearing a Tigger costume, ate a winnie the pooh doll.”

Postconditions for hash-invert

;;; Procedure:
;;;   hash-invert
;;; Parameters:
;;;   hash, a hash table
;;; Purpose:
;;;   Invert the hash table
;;; Produces:
;;;   inverted, a hash table
;;; Preconditions:
;;;   [No additional]
;;;   For any two keys, k1, k2, (hash-ref hash k1) is not equal to
;;;     (hash-ref hash k2).  That is, keys can't have duplicate values.
;;; Postconditions (with some repetition):
;;;   * If hash is empty, inverted is also empty
;;;   * For all keys, k, in hash:
;;;     If (hash-ref hash k) = v, then (hash-ref inverted v) = k
;;;   * For all keys, k, in hash:
;;;     (hash-ref inverted (hash-ref hash k)) = k
;;;     [another way to say the previous postcondition]

Suppose hash is #hash(("A" . "Apple") ("B" . "Banana")), what is a hash table that meets the postconditions, but is not what you would call an inverse of the original?

#hash(("Carrot" . "C") ("Apple" . "A") ("Banana" . "B"))

How could we write a postcondition that addresses that issue?

;;;   * The number of keys in `inverted` must be the same as the
;;;     number of keys in `hash`.  
;;;     (= (length (hash-keys inverted)) (length (hash-keys hash)))

How would you write the postconditions if we allowed two keys in hash to have the same value?

#hash(("Apple" . "A") ("Aardvark" . "A") ("Banana" . "B")))

What is the inverse of that table?

#hash(("A" . "Apple") ("B" . "Banana"))

or

#hash(("A" . "Aardvark") ("B" . "Banana"))
;;; Postconditions:
;;;   * If two or more keys in hash have the same value, then
;;;     inverted will have a pair with that value as its key
;;;     and one of those keys as its value. [Sam worries that this
;;;     is incomplete.]
;;;   * For all keys, k, in inverted:
;;;     (hash-ref keys (hash-ref inverted k)) = k
;;;   * If (hash-ref hash k) = v for some k, then v is a key of
;;;     inverted.
;;;   * inverted has no other keys.

Documenting sort-by-length

;;; Procedure:
;;;   sort-by-length
;;; Parameters:
;;;   strings, a list of strings [verified]
;;; Purpose:
;;;   Sort strings from shortest to longest; if two strings have
;;;   the same length, they should be in ASCII order.
;;; Produces:
;;;   sorted, a list
;;; Preconditions:
;;;   * strings must not be empty
;;; Postconditions:
;;;   * (= (length sorted) (length strings))
;;;   * If str2 appears immediately after str1 in sorted, then
;;;     Either (string-length str1) < (string-length str2)
;;;     Or the lengths are equal and (string-ci<=? str1 str2)
;;;   * The elements in sorted are the same as the elements in strings.

SamR’s solution if we don’t have the third precondition

(define sort-by-length
  (lambda (strings)
    (make-list (length strings) "done")))

Note: We could replace the first and third postconditions with

;;;   * sorted is a permutation of strings

Improving style

  1. Lines should not be more than 80 characters.
(string-append "once upon a time " (generate-something "hello" "gooodbye") " there was a " (generate-noun) " ... ")
  1. Spaces or newlines belong between the parameters to a procedure.
(string-append"hello"(generate)" and "(generate)) ; bad
(string-append "hello" (generate) " and " (generate)) ; good
(string-append "hello" 
               (generate) 
               " and " 
               (generate)) ; better 
  1. Traditionally, either all parameters on one line, or each parameter on a separate line. (There are a few cases in which things are clearer if you put multiple parameters on one line.)
(list-ref
 (list-ref
  (some complex expression that was very very long longer) 1) 0) ; BAD
  1. Names should be descriptive.

  2. Follow Racket indentation guidelines. (Force Racket to reindent.)

Individual questions

How do you verify that everything in a list is a string?

(all string? strings)

Notes on recursion