EBoard 23: Recursive Randomness

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]
  • Higher-order tail recursion [~10 min]
  • Q&A [~10 min]
  • Quiz [~10 min]
  • Lab [~50 min]

Administrative stuff

Notes and news

  • I’m working on cutting back a little in CSC 151.
    • I’ve cut part the third from MP4.
    • I’ve cut the most complex of the remaining topics (regular expressions)
    • I may cut an LA or two. (As a Celtics fan, I’d rather just cut LA.)
  • Mini-Project 5 has been posted.
  • In working with you individually, I am reminded once again of why I ask you to write tests first. Tests also give us examples to think through. And tests often help you identify base cases for recursion.
  • The magic recursion fairy varies what they say. But it’s usually some variant of “Assume your procedure works for all ‘smaller’ inputs. How does that help?
    • The magic recursion fairy notes that it’s sometimes good to ask this question with a particular input. (E.g., one of your test cases.)

Be kind to your graders

  • Good documentation. The explanation should be what, not how, and should be short. Document types correctly.
    • This prepares them to read.
  • Well-formatted code.
  • Refactor when possible. The less code to read, the easier it is to read. (In addition: The less code there is, the easier it is to maintain.)

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 tonight.
  • Individual tutors are temporarily unavailable. We’re recruiting more.
    • Don’t let this stop you from requesting one.
    • If you’re willing to share your individual tutor (as in, to do pair tutoring), let me know.

Upcoming activities

Attend (or watch recording) and send a one-paragraph reflection.

  • 5:00 p.m. Thursday, 10 December 2020: MIST: The Mathematical Image Synthesis Toolkit. Sam’s research students.

Upcoming work

  • Mini-project 4 (due TONIGHT, December 2 at 10:30 p.m. CST)
    • Call it mp4.rkt.
  • Redo of Mini-project 3 (due Sunday the 6th at 10:30 p.m. CST)
    • Returned today
  • Mini-project 5 (due next Wednesday, December 9 at 10:30 p.m. CST)
    • Call it mp5.rkt.
  • Reading for Thursday
  • Quiz today: Tail recursion
  • Quiz Thursday: Randomness

Higher-order tail recursion

Yesterday, we wrote the following procedure.

(define my-filter
  (lambda (pred? lst)
    (cond
      [(null? lst)
       null]
      [(pred? (car lst))
       (cons (car lst) (my-filter pred? (cdr lst)))]
      [else
       (my-filter pred? (cdr lst))])))

Can we use tail recursion?

Of course.

Time for random recitation.

What are the basic ideas of tail recursion?

  • Recurse “from the other end”.
  • How? Do computation before you do the recursive call, not after.
  • Code strategy? It’s often helpful to have an accumulator, which we almost always call so-far.
  • Anything else? We put that accumulator in a helper function.
  • Anything else? We call the helper function with some basic value. (Empty list if we’re building lists, empty string if we’re building strings, 0 or 1 if we’re building numbers.)
  • Anything else? In the base case, we return the accumulator (or some variant)

What are the basic ideas of higher-order recursion?

  • It seems a lot like normal recursion
  • Except … feels a little more difficult
  • More difficult because … we are passing a procedure as a parameter
    • Not much more difficult.
    • Just need to be used to procedures as parameters.
    • May feel more difficult
  • Higher-order recursion is useful because it’s more general
;;; (remove val lst) -> list?
;;;   val : any?
;;;   lst : list?
;;; Remove all copies of val from lst
(define remover
  (lambda (val lst)
    (cond 
      [(null? lst)
       null]
      [(not (equal? (car lst) val))
       (cons (car lst) (remover val (cdr lst)))]
      [else
       (remover val (cdr lst))])))

(check-equal? (remover "um" '("um" "the" "list" "of" "examples" "um"))
              '("the" "list" "of" "examples")
              "Edge case: thing to remove is at both ends")
(check-equal? (remover 7 '(3 4 5 7 7))
              '(3 4 5)
              "Multiple copies at end")
(check-equal? (remover 5 '(0 5 5 0))
              '(0 0)
              "Bon, Jame Bon")
(check-equal? (remover 5 '())
              '()
              "Empty list")
(check-equal? (remover 5 '(1 2 3))
              '(1 2 3)
              "Not present")

Can we write the body of remover as a call to filter?

(define remover
  (lambda (val lst)
    (let ([pred? (lambda (x) (not (equal? x val)))])
      (my-filter pred? lst))))

Important lesson: Once we’ve written my-filter, we may never again have to write that pattern of recursion; we can just call my-filter.

If we think of a better way to write my-filter, such as a tail-recursive version, we get the gain everywhere, not just in one procedure.

The point here is that if DrRacket did not include filter, we could write it ourselves using simpler things (if, car, cdr). You are empowered.

(define my-filter-helper
  (lambda (pred? lst so-far)
    (cond
      [(null? lst)
       (reverse so-far)]
      [(pred? (car lst))
       (my-filter-helper pred? (cdr lst) (cons (car lst) so-far))]
      [else
       (my-filter-helper pred? (cdr lst) so-far)])))

(define my-filter
  (lambda (pred? lst)
    (my-filter-helper pred? lst null)))

Why not use letrec?

I find it better to start by writing a separate helper.

Note: Tail recursion tends to reverse lists, so we need to reverse them again.

Q&A

Mini-Project 3 Redo

What counts as a non-trivial procedure?

Not nearly identical to the earlier problems.

Mini-Project 4

If I spend one token do I get until Friday night?

Yes

Are there evening tutors on Friday night?

No

Will Sam be online Friday night?

Probably not

Please explain the following

; A non-empty list whose first element is 2
> (find-match '(join 2 x)
              '(((1 2) 3) (2 3) (2 1) (1 4) 5))
'(2)
  • Does '(join 2 x) match the whole value? No.
  • Is the value a list? Yes.
  • Does '(join 2 x) match somewhere in the car of the whole value? ‘((1 2) 3)
    • Does ‘(join 2 x)` match the whole value? No.
    • Is the value a list? Yes.
    • Does ‘(join 2 x)` match somewhere in the car of the value? ‘(1 2)
      • Does ‘(join 2 x) match the whole value ‘(1 2)? No
      • Is the value a list? Yes
      • Does ‘(join 2 x) match somewhere in the car? 1
        • Does ‘(join 2 x) match 1? No.
        • Is 1 a list No.
        • This path failed.
      • Does ‘(join 2 x) match somewhere in the cdr? ‘(2)
        • Does ‘(join 2 x) match ‘(2)? Yes! We’re done! We return 2.
    • Does ‘(join 2 x)` match somewhere in the cdr of the value? ‘(3)
  • Does '(join 2 x) match somewhere in the cdr of the whole value? ‘((2 3) (2 1) (1 4) 5)

There’s an implicit list that starts with 2.

We build '(1 2) implicitly as (cons 1 (cons 2 null))

If the pattern is (list 1 2 3 x), it should return the first list/sublist/subsublist/etc, that has the form '(1 2 3 x) (four element list that starts with 1 2 3).

It always returns the first/leftmost matching thing, if there is one.

It sounds like there’s recursion involved here.

Yes.

Does that mean find-match should do its own recursion?

Yes. Recursion is awesome!

Do we have to return all instances?

No, just the first.

But if you wanted to write another procedure that returns all matches, the graders would enjoy reading that.

If we wanted to return all matches, what should we do?

Your goal should be to return a list of matches, rather than a match or #f. If there are not matches, return the empty list.

Append all the matches in the car to all the matches in the cdr. Add the whole thing on the front if it matches.

For MP4, should we use all the given tests and, if so, can we keep them as is?

You should use the given tests.

No, they don’t have to be a test suite. Sam likes “Include them and cross your fingers.”

Tail recursion

I find it hard to jump to tail recursion. Is that okay? Tips for jumping straight to tail recursion?

Practice.

Higher-order recursion

Can we talk about fold?

Not today.

Randomness

Why does random with no params work?

It’s designed that way, even though the meaning is slighty different. Some folks like to make procedures work in multiple ways depending on how many or what type of parameters they are given.

Why does (random 0) not work?

Random comes up with a number between 0 (inclusive) and parameter (exclusive).

There no numbers between 0 and 0 that are not 0.

Miscellaneous

Are we doing randomness tomorrow?

Yes.

Quiz

Reminder: I recommend the following strategy for quizzes

  • Read the problem.
  • Enter your first guess in Gradescope.
  • Click “Submit”.
  • Copy and paste over to DrRacket and try to fix.
  • Go back to Gradescope and resubmit.

If you have not already done so …

  • Open the reading, your notes, and your lab on tail recursion.
  • Open DrRacket.
  • Go to Gradescope and take the quiz.

Lab

The lab will continue tomorrow!

Please only do the A side today. (That makes it easier if we have a different set of students here tomorrow.)