Functional Problem Solving (CSC 151 2015S) : EBoards

CSC151.01 2015S, Review Session Week 9: Local Procedures and Turtles


Overview

Your Questions

What is going on with that strange "organic" drawing from yesterday's lab?

Sam will try to simulate this in ASCII, which is kind of limited.

Here's a simulation of a five step spiral

    --\
      |
     -

Three step spiral

    --\

Add a four step spiral

    --\
       \
        \
         |
        /

Add a five step spiral

    --\
       \
        \
         |
        /
     / /
    | /
    \-

Now, let's look at the code again (done in DrRacket)

    (for-each (l-s turtle-spiral! tommy5)
                (map (l-s + 10) (iota 10)))`

This makes a spiral of length ten, then a spiral of length 11, then a spiral of length 12, ..

    Welcome to DrRacket, version 5.3.6 [3m].
    Language: racket; memory limit: 128 MB.
    > (define world5 (image-show (image-new 400 400)))
    > (define tommy (turtle-new world5))
    > (turtle-teleport! tommy 200 200)
    #<procedure:...igls/turtles.rkt:42:6>
    > ; (for-each (l-s turtle-spiral! tommy5)
      ;          (map (l-s + 10) (iota 10)))
       (turtle-spiral! tommy 10)
    > ; (for-each (l-s turtle-spiral! tommy5)
      ;          (map (l-s + 10) (iota 10)))
       (turtle-spiral! tommy 11)
    > ; (for-each (l-s turtle-spiral! tommy5)
      ;          (map (l-s + 10) (iota 10)))
       (turtle-spiral! tommy 12)
    > ; (for-each (l-s turtle-spiral! tommy5)
      ;          (map (l-s + 10) (iota 10)))
       (turtle-spiral! tommy 13)
    > ; (for-each (l-s turtle-spiral! tommy5)
      ;          (map (l-s + 10) (iota 10)))
       (turtle-spiral! tommy 14)
    > ; (for-each (l-s turtle-spiral! tommy5)
      ;          (map (l-s + 10) (iota 10)))
       (turtle-spiral! tommy 15)
    > ; (for-each (l-s turtle-spiral! tommy5)
      ;          (map (l-s + 10) (iota 10)))
       (turtle-spiral! tommy 16)
    > (turtle-teleport! tommy 200 200)
    #<procedure:...igls/turtles.rkt:42:6>
    > (turtle-face! tommh 0)
    . . tommh: undefined;
     cannot reference an identifier before its definition
    > (turtle-face! tommy 0)
    #<procedure:...igls/turtles.rkt:42:6>
    > (turtle-set-color! tommy "red")
    #<procedure:...igls/turtles.rkt:42:6>
    > (turtle-spiral! tommy 10)
    > (turtle-spiral! tommy 10)
    > (turtle-spiral! tommy 10)
    > (turtle-spiral! tommy 10)
    > (turtle-spiral! tommy 10)
    > (turtle-spiral! tommy 10)
    > (turtle-spiral! tommy 10)
    > (repeat 10 (r-s turtle-spiral! 10) tommy)

Can we review letrec?

Situation: We need one or more recursive helper procedures.

Normal model

    (define proc
      (lambda (...)
        (helper ...)))

    (define helper
      (lambda ...)
        (if (...)
            (...)
            (helper ...)))

Good design: Don't give other programmers access to your helpers. (1) You may have assumptions that won't understand. (2) The design of some versions of Scheme (although not Racket) lets them replace the helper to change behavior (intentionally or unintentionally).

    (define silly
      (lambda (x)
        (display "It's so silly that ")
        (foo x)))

    (define foo
      (lambda (xa)
        (display x)
        (newline)))

    > (silly "Sam is awake")
    It's so silly that Sam is awake
    > (define foo
        (lambda (x)
          (display "Grinnell is making Scarlett its mascot")
          (newline)))
    > (silly "Sam is awake")
    It's so silly that Grinnell is making Scarlett its mascot

So the helper should be local. Here's one model.

    (define proc
      (letrec ([helper
                (lambda ...)
                  (if (...)
                      (...)
                      (helper ...))])
        (lambda (...)
          (helper ...)))

This also makes it easier to write helpers because they can now refer to any of the parameters of the original procedure..

    (define proc
      (lambda (x ...)
        (letrec ([helper
                  (lambda ...)
                    (if (...)
                        (... x ...)
                        (helper ...))])
            (helper ...)))

Can we review named let?

Scheme programmers saw the following common case: (1) I want to write to write one recursive helper and (2) I know the parameters. Let's come up with a syntax that is more like the way we think about solving the problem.

Example: Tallying odd numbers. We usually try to understand the problem with a particular example, e.g., "count the number of odd numbers in (5 1 2 4 3)

Step: Build a "table"

    tally   remaining
    0       (5 2 1 4 3)     ; First number is odd
     add 1   cdr
    1       (2 1 4 3)       ; First number is even
     add 0   cdr
    1       (1 4 3)

Think about those first two lines. "I want two columns, tally (starts at 0) and remaining (starts as the whole list."

Using letrec

    (define tally-odds
      (letrec ([helper
                (lambda (tally remaining) 
                  (if (...)
                      (...)
                      (helper ...)))])
        (lambda (ints)
          (helper 0 ints)))))

The letrec model disconnects the two ideas.

Solution? Create another model that keeps the ideas connected

    (define tally-odds
      (lambda (ints)
        (let helper ; I want a procedure named helper
              ([tally 0]    ; Start the tally at 0
               [remaining ints])     ; Start remaining as the whole list
          (cond
            [(null? remaining)
             tally]
            [(odd? (car remaining))
             (helper (+ 1 tally) (cdr remaining))]
            [else
             (helper tally (cdr remaining))]))))

About the Quiz

Can we look at sample problems on the quiz?

Main topics:

Local bindings?

Turtles

Repetition with repeat and for each

Let's try the husk and kernel one.

Start with existing code.

    (define tally-odds
      (lambda (ints)
        (let helper ; I want a procedure named helper
              ([tally 0]    ; Start the tally at 0
               [remaining ints])     ; Start remaining as the whole list
          (cond
            [(null? remaining)
             tally]
            [(odd? (car remaining))
             (helper (+ 1 tally) (cdr remaining))]
            [else
             (helper tally (cdr remaining))]))))

When will this fail?

Let's start with those

    (define tally-odds
      (lambda (ints)
        (cond
          [(not (list? ints))
           (error "tally-odds, expects a list as a parameter, received" ints)]
          [(not (all integer? ints))
           (error "tally-odds, expects a list of INTEGERS as a parameter, reeived" ints)]
          [else
            (let helper ; I want a procedure named helper
                  ([tally 0]    ; Start the tally at 0
                   [remaining ints])     ; Start remaining as the whole list
              (cond
                [(null? remaining)
             tally]
                [(odd? (car remaining))
                 (helper (+ 1 tally) (cdr remaining))]
                [else
                 (helper tally (cdr remaining))]))])))