EBoard 31: Sorting, Speed, and Some SoLA Stuff

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

Approximate overview

  • General administrative stuff [~10 min]
  • Q&A [~10 min]
  • Quiz [~10 min]
  • Reverse, revisited [~10 min]
  • Memory Models [~10 min]
  • Largest in List [~10 min]
  • Fun with trees [~10 min]
  • On Sorting [Whatever’s Left]

Administrative stuff

General Notes

  • Additional office hours (email or DM to reserve times)
    • Today, 2:30-4:30 (2:30-4:00 plus OH claimed)
    • Tuesday, 2:30-4:00 (2:30-3:00 and 4:00-4:30 claimed) plus 1:30-2:30 OH (almost all claimed).
    • Wednesday, 2:30-4:30 plus 10:00-11:00 OH
    • Thursday, 2:30-4:30 plus 10:00-11:00 OH
    • Those in other time zones can request evening hours.
  • I’ll also continue to hang out on Teams and email when I can.
  • Mentor sessions tonight at 5 and 9.
  • Apologies for Friday’s lab. That will teach me to rewrite the lab and reading the day before class.
  • There are some things on Gradescope with incredibly late late due dates. Those are there by special arrangment. Please don’t take advantage of them unless you’ve made special arrangements.
    • But feel free to reach out to me if you need special arrangements.
    • The second redos are open to anyone (and cost one token)
  • Update to syllabus: I’ve clarified what happens if you over-use tokens. [Basically, you lose quizzes, readings, or labs, whichever harms you the last.)
  • In case it wasn’t clear …
    • We provide long lists of things to correct on the MPs so that you have enough detail to get to E the next time (or at least we hope you can).
    • We show you the whole rubric we’ve used so that you can consider not just the issues you’ve had (indicated by check marks), but also issues others have had (so that you don’t introduce those issues next time).
    • Our goal is to get everyone to 28 LAs and all Es on the MPs.
  • Course evaluations will be in class on Thursday.
  • Attendance in Thursday’s class is expected.
  • Welcome to the new Teams.

Notes on SoLA 4

  • Sample problems released.
    • Some are likely longer or more complicated that those on the SoLA.
    • There are likely to be some similarities.
  • Everyone gets 60 minutes for every problem. (But you should try to finish in 20.)
  • There will be repeats of all problems except (a) Primitive Types (from Group 1) and (b) Collaboration (also from Group 1). [If you still need one of those two, let me know.]
  • I will do my best to grade it as speedily as I can so that you can decide whether or not to take the last SoLA (and what to study for).

Tutoring reminders

  • Please use our tutors! They like to work.
  • Evening tutoring will be available
    • 8-10 p.m. Sundays through Thursdays
    • 3-5 p.m. Sundays
    • In the “Drop in tutoring” channel on the CS team.
    • Sam will generally not be available during these times.
    • We should have two evening tutors on Wednesdays.
    • Don’t forget to cite the evening tutors (and the individual tutors).

Upcoming activities

Info:

  • Attend (or watch recording within a day or so) and send a one-paragraph reflection asap afterwards.
  • Only those activities I list count.
  • But you can suggest others.
  • Links are in the Announcements channel.
  • Unless otherwise specified, these each earn one token.

Activities

  • 12:00 m. TODAY, 14 December 2020: CS Table (Google vs. Wikipedia)
  • 5:00 p.m. TODAY, 14 December 2020: Mentor Session
  • 9:00 p.m. TODAY, 14 December 2020: Mentor Session
  • 5:00 p.m. Thursday, 17 December 2020: Thinking ahead until summer (tentative)
  • 12:00 m. Monday, 21 December 2020: CS Table

Upcoming work

Mini Projects

  • Redo of Mini-project 5 due Sunday, December 20 at 10:30 p.m. CST
  • Re-Redo of any other mini projects (costs one token) due Tuesday, December 22 at 11:59 p.m. CST

Readings

  • Readings for Tuesday
  • And that’s it!

Quizzes

  • Quiz Today: Binary Search, Analysis, etc.
  • Quiz Tuesday: Sorting [OUR LAST QUIZ]

Other

  • SoLA 4 Wednesday (note different day of week)

A quick note from Friday’s lab

Why was one of the reverse implementation so much slower than the other?

;;; (list-append l1 l2) -> list?
;;;   l1, l2 : list?
;;; Returns the list formed by placing the elements of l2 after the elements
;;; of l1, preserving the order of the elements of l1 and l2.
(define list-append
  (lambda (l1 l2)
    (write (list 'list-append l1 l2)) (newline)
    (cond
      [(null? l1)
       l2]
      [else
       (cons (car l1)
             (list-append (cdr l1) l2))])))

;;; (list-reverse l) -> list?
;;;   l : list?
;;; Returns l with the elements in the opposite order.
(define list-reverse-1
  (lambda (l)
    (match l
      ['()
       null]
      [(cons x tail)
       (list-append (list-reverse-1 tail) (list x))])))

(define list-reverse-2
  (lambda (l)
    (letrec ([helper
              (lambda (l so-far)
                (match l
                  ['()
                   so-far]
                  [(cons x tail)
                   (helper tail (cons x so-far))]))])
      (helper l null))))

What did we discover about list-reverse-1 and list-reverse-2?

  • list-reverse-1 takes a lot more time than list-reverse-2.
  • list-reverse-2 ahd approximately n recursive calls for a list of length n.
  • list-reverse-1 also had approximately n recursive calls for a list of length n.
  • However, list-reverse-1 calls list-append, list-append is recursive, and so there are lots of calls to list-append.
  • We’ll look at the particular details after the quiz.

We also studied alphabetically-first

  • One of them did really poorly on lists that were arranged last to first.

Q&A

SoLA 4

Will the documentation SoLA be easier to finish this time?

I hope so. Fewer subtleties, but still some.

Are there seven new questions for SoLA 4?

Yes. Read tree recursion. Write tree recursion. Analysis. Searching and Sorting. Divide and conquer. Computational thinking.

For the Information Hiding one, which one should we change?

Only the ones whose code has to change.

I don’t feel like I have enough experience writing tree procedures. Beyond the practice, what can I do?

Invent your own problems. What’s something you might do with a tree? Think about things we’ve done with lists that we haven’t (yet) done with trees. Note: tree-remove is hard. Non-binary trees are hard. Other: tally (specific or general), mapping (specific or general), filtering (removing is hard, but you could filter into a list), convert to a vector.

Redo ones we’ve done already: tree-size, tree-depth, tree-sum, tree->list, tree-max, tree-level, tree-contains, bst-find, …

Are there trees other than binary trees?

Yes! Definitely. If you think about the original example of a corporate hierarchy, the president usually has more than two vice-presidents.

Biological trees.

Administrative stuff

How many total quizzes / lab writeups / readings will there be?

Sam needs to update the count.

We have the beginning-of-semester assumptions posted. Sam will scale.

How can we tell how many tokens we have?

You can’t. It’s a secret.

Over-use: You’ve done more things that require tokens you have.

If you over-use it gets used against labs, readings, whatever.

What costs tokens?

Arriving to class after John or Nameera has taken attendance. (Usually about 8 min past the start of class.)

Missing class without a clear reason/explanation. (Costs 2)

Late mini-projects (or late quizzes or late labs or …) without pre-arrangement/rationale.

Second redo on mini-projects.

I went to CS Table in Week 2. Can I still send the reflection.

Agh!

Sure.

Are there still things that earn tokens?

Yes, see above.

Friday’s lab

Other Questions

  • Used to search in sorted vectors.
  • Mechanism: Look in the middle of the range of interest.
    • Found! Done
    • The thing in the middle is too “large”: Recurse to the left
    • The thing in the middle is too “small”: Recurse to the right
  • We keep track of the range of interest by using a lower-bound (lb) and an upper-bound (ub).
    • When looking, we include lower-bound, but not upper-bound
    • We initialize lower-bound to 0 and upper-bound to vector-length
  • This is an example of “divide and conquer”. We can search a sorted vector of length 1000 with about ten comparison using binar search.
  • Our base case is when lb >= ub.

Quiz

Cancelled. Maybe Sam will fix it up for the SoLA.

Sorting

  • A fairly straighforward problem (I hope)
  • The goal is to put things in order, from “smallest” to “largest” (or “largest” to “smallest”)
  • For real numbers: Order numerically
  • For strings: Order alphabetically
  • For students: Order by name, GPA, student id, height, …
  • Useful to prepare data for binary search.
  • Useful for other reasons, too, such as putting similar things nearby, perhaps so that we can break into groups.
  • How do we sort?
  • Sam’s suggestion:
    • Think about how you might sort something particular.
    • Generalize.
    • Experiment with your generalization.
      • Redo if it doesn’t work
      • Fine tune if it does
    • Turn into Racket/Scheme
    • Experiment/test
    • Analyze! (Is it fast? Is it fast enough?)
  • Our goal is to do that today.
  • We sort vectors
  • We sort lists
  • What operations seem reasonable as the basic steps in sorting a vector?
    • Set one of the elements
    • Grab one of the elements and put it to the side.
    • Compare two elements by position (if the letter at position X is less than the letter at position Y, then …)
    • Swap two elements
  • What operations seem reasonable as the basic steps in sorting a list?
    • Take the car
    • Take the cadr
    • Compare two elements we’ve gotten by taking the car and the cdr
    • Compare another element we’ve grabbed to the car
    • Check if its empty or singleton
    • Build a new list with cons and null
    • Anything we can build from those
  • Your goal: Write two different sorting algorithms, one for vectors, one for lists, using a different strategy, if possible.
  • You’ll have about ten minutes.

Vectors, Idea 1

  • Compare each element to the next one. If not in order, swap.
    • After round 1, the largest element is at the right.
  • Keep doing that until they are sorted.

How long does this take?

  • One round, with n people, requires up to n swaps
  • With n people, we need n-1 rounds
  • So about n*(n-1) steps in the worst case.
  • This is an O(n*n) algorithm.

Vectors, Idea 2

  • Find the largest element. Swap it to the end.
  • Find the next largest element (e.g., by keeping track)
  • Repeat!
  • Note: We need a barrier to keep track of what we’ve dealt with and haven’t dealt with.

Vectors, Idea 3

  • Shuffle the vector elements randomly.
  • Check if they are sorted.
  • If so, stop.
  • If not, go back to the top and shuffle again.

Lists, Idea 1

  • If already in order, we’re done.
  • After that, still some complexities

Lists, Idea 2

  • Find the largest value in the list (e.g., with apply max)
  • Remove it from the list
  • Put it in a new list.

Lists, Idea 3

  • Repeatedly
    • Grab the first element from the list
    • Put it into the new list.

SoLA 3: Memory Models

We did not cover this issue.

On immutable lists

Here’s a sample list.

(define cat (list "c" "a" "t"))

        +---+---+    +---+---+    +---+---+
cat --> | * | -----> | * | *----> | * | / |
        +-|-+---+    +-|-+---+    +-|-+---+
          v            v            v
         "c"          "a"          "t"

Here’s an attempt to “change” that list (build a new list)

(define hat (cons "h" (cdr cat)))




        +---+---+    +---+---+    +---+---+
cat --> | * | -----> | * | *----> | * | / |
        +-|-+---+    +-|-+---+    +-|-+---+
          v            v            v
         "c"          "a"          "t"

What has happened?

On let

Incorrect comment from some students: “The let introduces a temporary change to a vector.”

Consider the following code.

;;; (vector-swap! vec i j) -> vec
;;;   vector : vector?
;;;   i : integer?
;;;   j : integer?
;;; Swap the elements at indices i and j of vec, returning the
;;;   now-modified vector.
;;; Pre: 0 <= i < (vector-length vec)
;;; Pre: 0 <= j < (vector-length vec)
(define swap
  (lambda (vec)
    (let ([tmp (vector-ref vec i)])
      (vector-set! vec i (vector-ref vec j))
      (vector-set! vec j tmp))))

;;; (vector-something! vec i) -> any?
;;;    vec : vector?
;;;    i : integer?
;;; Swap elements at positions 0 and i, returning the old value
;;;   at position i.
;;; Pre: 0 <= i < (vector-length vec)
(define vector-something!
  (lambda (vec i)
    (let ([newvec (vector-swap! vec 0 i)])
      ; Temporary: Verify that the vector changed
      (write newvec) (newline) 
      (vector-ref vec 0)))) 

What do we expect for the following?

> (define sample (vector "c" "a" "t")

Largest in List

We did not cover this issue.

Why was alphabetically-first so slow for lists arranged last to first?

;;; (alphabetically-first string) -> string
;;;   strings: both (listof string?) nonempty?
;;; Find the alphabetically first string in the list.
(define alphabetically-first-1
  (lambda (strings)
    ; (counter-increment! AFC 'alphabetically-first-1)
    (cond
      [(null? (cdr strings))
       (car strings)]
      [(string-ci<=? (car strings) (alphabetically-first-1 (cdr strings)))
       (car strings)]
      [else
       (alphabetically-first-1 (cdr strings))])))

(define alphabetically-first-2
  (lambda (strings)
    ; (counter-increment! AFC 'alphabetically-first-2)
    (if (null? (cdr strings))
        (car strings)
        (first-of-two (car strings)
                      (alphabetically-first-2 (cdr strings))))))

;;; (first-of-two str1 str2) -> string?
;;;   str1 : string?
;;;   str2 : string?
;;; Find the alphabetically first of str1 and str2.
(define first-of-two
  (lambda (str1 str2)
    (if (string-ci<=? str1 str2)
        str1
        str2)))

Fun with Trees

We did not cover this issue.

Aka: Making your brain hurt a little bit more.

Our normal pattern for tree recursion.

(define tree-proc
  (lambda (tree)
    (if (empty-tree? tree)
        base-case
        (combine (process (btt tree))
                 (tree-proc (btl tree))
                 (tree-proc (btr tree))))))

For tree-size,

  • base-case is
  • combine is
  • process is
(define tree-proc
  (lambda (tree)
    (if (empty-tree? tree)
        base-case
        (combine (process (btt tree))
                 (tree-proc (btl tree))
                 (tree-proc (btr tree))))))

For tree-sum

  • base-case is
  • combine is
  • process is
(define tree-proc
  (lambda (tree)
    (if (empty-tree? tree)
        base-case
        (combine (process (btt tree))
                 (tree-proc (btl tree))
                 (tree-proc (btr tree))))))

For tree->list

  • base-case is
  • combine is
  • process is
(define tree-proc
  (lambda (tree)
    (if (empty-tree? tree)
        base-case
        (combine (process (btt tree))
                 (tree-proc (btl tree))
                 (tree-proc (btr tree))))))

For tree-increment

  • base-case is
  • combine is
  • process is
(define tree-proc
  (lambda (tree)
    (if (empty-tree? tree)
        base-case
        (combine (process (btt tree))
                 (tree-proc (btl tree))
                 (tree-proc (btr tree))))))

Hmm … we’re doing a lot of boring, repetitive work. Isn’t that what computers are good for?

;;; (make-tree-proc base-case combine process) -> procedure?
;;;    base-case : value
;;;    combine : procedure?
;;;    process : procedure?
;;; Create a procedure for processing trees using the standard template
(define make-tree-proc
  (lambda (base-case combine process)
    (letrec [(tree-proc
              (lambda (tree)
                (if (empty-tree? tree)
                    base-case
                    (combine (process (btt tree))
                             (tree-proc (btl tree))
                             (tree-proc (btr tree))))))]
      tree-proc)))

Does this really work?

Let’s see.

(define tree-size
  (make-tree-proc 0 + (lambda (x) 1)))
(define tree-sum
  (make-tree-proc 0 + (lambda (x) x)))
(define tree->list
  (make-tree-proc null append list))
(define tree-increment
  (make-tree-proc empty-tree btn (section + 1 <>)))
(define tree-depth
  (make-tree-proc 0 (lambda (x y z) (+ 1 (max y z))) (lambda (x) 0)))