6.001/Recitation 6

Rob Speer

Contents

More flavors of let

let* expands into a series of nested lets, so that you can use definitions that depend on earlier definitions.

(let* [(x 4)
       (y (* x x)]
  (list x y))

letrec lets you use lets that refer to each other, as long as they don't need the values right away. Here's one example of it:

(letrec ((even?
          (lambda (n)
            (if (zero? n)
                #t
                (odd? (- n 1)))))
         (odd?
          (lambda (n)
            (if (zero? n)
                #f
                (even? (- n 1))))))
  (even? 88))

A let statement can be given a name, in which case it can call itself recursively with new values. Schemers love this for some reason. Here's how last-pair is actually implemented in MzScheme (the implementation we use in DrScheme):

 (define last-pair
   (polymorphic
    (lambda (l)
      (if (pair? l)
          (let loop ((l l) (x (cdr l)))
            (if (pair? x)
                (loop x (cdr x))
                l))
          (raise-type-error 'last-pair "pair" l)))))

No, I don't know what the "polymorphic" part means.

An extension to MzScheme allows the mother of all lets, where values can immediately refer to earlier values, later values, or themselves. So here's a cool way to make a circular list:

(require (lib "shared.ss"))
(define circ (shared ([a (cons 1 b)]
                      [b (cons 2 a)]))

circ is now the circular list (1 2 1 2 1 2 ...)

  • Strange question. Is a circular list a list? How do you know? What does DrScheme think?

Quoting

(quote expr) takes an expression and, instead of evaluating it, returns a value that prints out as that expression. As a very common example, you can use (quote (1 2 3)) to get essentially the same result as (list 1 2 3).

  • If you quote a self-evaluating value, you get that value.
  • If you quote a parenthesized expression, you get a list.
  • If you quote a name, you get an interned reference to that name, which we call a symbol.

Quote is so important to Scheme that there's a shortcut for it in the syntax: a single apostrophe with the parentheses omitted. This is transformed, even before syntactic desugaring takes place, into the equivalent (quote) expression.

Problems

Start with these definitions:

(define a '(1 2))
(define b 'a)

What do these return?

(a b)


(list a b)


'(a b)


(car 'a)


(car ''a)


Lexical scope

The scope of a name is the area of code where it's defined. Scope always works by where in the code the definition occurs, not when it is executed! You can take advantage of this to store state in procedures.

Here's three possible versions of a procedure that "instruments" another procedure to count the number of times it's used. What are the effects of each?

(define make-count-proc-1
  (lambda (f)
    (lambda (x)
      (let ((count 0))
        (cond ((eq? x 'count) count)
              (else
               (set! count (+ count 1))
               (f x)))))))

(define sqrt* (make-count-proc-1 sqrt))
(sqrt* 4)
(sqrt* 'count)


(define make-count-proc-2
  (lambda (f)
    (let ((count 0))
      (lambda (x)
        (cond ((eq? x 'count) count)
              (else
               (set! count (+ count 1))
               (f x)))))))

(define sqrt* (make-count-proc-2 sqrt))
(define square* (make-count-proc-2 square))
(sqrt* 4)
(sqrt* 'count)
(square* 4)
(square* 'count)


(define make-count-proc-3
  (let ((count 0))
    (lambda (f)
      (lambda (x)
        (cond ((eq? x 'count) count)
              (else
               (set! count (+ count 1))
               (f x)))))))

(define sqrt* (make-count-proc-2 sqrt))
(define square* (make-count-proc-2 square))
(sqrt* 4)
(sqrt* 'count)
(square* 4)
(square* 'count)