|
|
|
|
# Local Binding
|
|
|
|
|
|
|
|
|
|
Although internal `define`s can be used for local binding, Racket
|
|
|
|
|
provides three forms that give the programmer more control over
|
|
|
|
|
bindings: `let`, `let*`, and `letrec`.
|
|
|
|
|
|
|
|
|
|
## 1. Parallel Binding: `let`
|
|
|
|
|
|
|
|
|
|
> +\[missing\] in \[missing\] also documents `let`.
|
|
|
|
|
|
|
|
|
|
A `let` form binds a set of identifiers, each to the result of some
|
|
|
|
|
expression, for use in the `let` body:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(let ([id expr] ...) body ...+)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The `id`s are bound “in parallel.” That is, no `id` is bound in the
|
|
|
|
|
right-hand side `expr` for any `id`, but all are available in the
|
|
|
|
|
`body`. The `id`s must be different from each other.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (let ([me "Bob"])
|
|
|
|
|
me)
|
|
|
|
|
"Bob"
|
|
|
|
|
> (let ([me "Bob"]
|
|
|
|
|
[myself "Robert"]
|
|
|
|
|
[I "Bobby"])
|
|
|
|
|
(list me myself I))
|
|
|
|
|
'("Bob" "Robert" "Bobby")
|
|
|
|
|
> (let ([me "Bob"]
|
|
|
|
|
[me "Robert"])
|
|
|
|
|
me)
|
|
|
|
|
eval:3:0: let: duplicate identifier
|
|
|
|
|
at: me
|
|
|
|
|
in: (let ((me "Bob") (me "Robert")) me)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The fact that an `id`’s `expr` does not see its own binding is often
|
|
|
|
|
useful for wrappers that must refer back to the old value:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (let ([+ (lambda (x y)
|
|
|
|
|
(if (string? x)
|
|
|
|
|
(string-append x y)
|
|
|
|
|
(+ x y)))]) ; use original +
|
|
|
|
|
(list (+ 1 2)
|
|
|
|
|
(+ "see" "saw")))
|
|
|
|
|
'(3 "seesaw")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Occasionally, the parallel nature of `let` bindings is convenient for
|
|
|
|
|
swapping or rearranging a set of bindings:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (let ([me "Tarzan"]
|
|
|
|
|
[you "Jane"])
|
|
|
|
|
(let ([me you]
|
|
|
|
|
[you me])
|
|
|
|
|
(list me you)))
|
|
|
|
|
'("Jane" "Tarzan")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The characterization of `let` bindings as “parallel” is not meant to
|
|
|
|
|
imply concurrent evaluation. The `expr`s are evaluated in order, even
|
|
|
|
|
though the bindings are delayed until all `expr`s are evaluated.
|
|
|
|
|
|
|
|
|
|
## 2. Sequential Binding: `let*`
|
|
|
|
|
|
|
|
|
|
> +\[missing\] in \[missing\] also documents `let*`.
|
|
|
|
|
|
|
|
|
|
The syntax of `let*` is the same as `let`:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(let* ([id expr] ...) body ...+)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The difference is that each `id` is available for use in later `expr`s,
|
|
|
|
|
as well as in the `body`. Furthermore, the `id`s need not be distinct,
|
|
|
|
|
and the most recent binding is the visible one.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (let* ([x (list "Burroughs")]
|
|
|
|
|
[y (cons "Rice" x)]
|
|
|
|
|
[z (cons "Edgar" y)])
|
|
|
|
|
(list x y z))
|
|
|
|
|
'(("Burroughs") ("Rice" "Burroughs") ("Edgar" "Rice" "Burroughs"))
|
|
|
|
|
> (let* ([name (list "Burroughs")]
|
|
|
|
|
[name (cons "Rice" name)]
|
|
|
|
|
[name (cons "Edgar" name)])
|
|
|
|
|
name)
|
|
|
|
|
'("Edgar" "Rice" "Burroughs")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In other words, a `let*` form is equivalent to nested `let` forms, each
|
|
|
|
|
with a single binding:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (let ([name (list "Burroughs")])
|
|
|
|
|
(let ([name (cons "Rice" name)])
|
|
|
|
|
(let ([name (cons "Edgar" name)])
|
|
|
|
|
name)))
|
|
|
|
|
'("Edgar" "Rice" "Burroughs")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 3. Recursive Binding: `letrec`
|
|
|
|
|
|
|
|
|
|
> +\[missing\] in \[missing\] also documents `letrec`.
|
|
|
|
|
|
|
|
|
|
The syntax of `letrec` is also the same as `let`:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(letrec ([id expr] ...) body ...+)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
While `let` makes its bindings available only in the `body`s, and `let*`
|
|
|
|
|
makes its bindings available to any later binding `expr`, `letrec` makes
|
|
|
|
|
its bindings available to all other `expr`s—even earlier ones. In other
|
|
|
|
|
words, `letrec` bindings are recursive.
|
|
|
|
|
|
|
|
|
|
The `expr`s in a `letrec` form are most often `lambda` forms for
|
|
|
|
|
recursive and mutually recursive functions:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (letrec ([swing
|
|
|
|
|
(lambda (t)
|
|
|
|
|
(if (eq? (car t) 'tarzan)
|
|
|
|
|
(cons 'vine
|
|
|
|
|
(cons 'tarzan (cddr t)))
|
|
|
|
|
(cons (car t)
|
|
|
|
|
(swing (cdr t)))))])
|
|
|
|
|
(swing '(vine tarzan vine vine)))
|
|
|
|
|
'(vine vine tarzan vine)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (letrec ([tarzan-near-top-of-tree?
|
|
|
|
|
(lambda (name path depth)
|
|
|
|
|
(or (equal? name "tarzan")
|
|
|
|
|
(and (directory-exists? path)
|
|
|
|
|
(tarzan-in-directory? path depth))))]
|
|
|
|
|
[tarzan-in-directory?
|
|
|
|
|
(lambda (dir depth)
|
|
|
|
|
(cond
|
|
|
|
|
[(zero? depth) #f]
|
|
|
|
|
[else
|
|
|
|
|
(ormap
|
|
|
|
|
(λ (elem)
|
|
|
|
|
(tarzan-near-top-of-tree? (path-element->string elem)
|
|
|
|
|
(build-path dir elem)
|
|
|
|
|
(- depth 1)))
|
|
|
|
|
(directory-list dir))]))])
|
|
|
|
|
(tarzan-near-top-of-tree? "tmp"
|
|
|
|
|
(find-system-path 'temp-dir)
|
|
|
|
|
4))
|
|
|
|
|
#f
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
While the `expr`s of a `letrec` form are typically `lambda` expressions,
|
|
|
|
|
they can be any expression. The expressions are evaluated in order, and
|
|
|
|
|
after each value is obtained, it is immediately associated with its
|
|
|
|
|
corresponding `id`. If an `id` is referenced before its value is ready,
|
|
|
|
|
an error is raised, just as for internal definitions.
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (letrec ([quicksand quicksand])
|
|
|
|
|
quicksand)
|
|
|
|
|
quicksand: undefined;
|
|
|
|
|
cannot use before initialization
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 4. Named `let`
|
|
|
|
|
|
|
|
|
|
A named `let` is an iteration and recursion form. It uses the same
|
|
|
|
|
syntactic keyword `let` as for local binding, but an identifier after
|
|
|
|
|
the `let` \(instead of an immediate open parenthesis\) triggers a
|
|
|
|
|
different parsing.
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(let proc-id ([arg-id init-expr] ...)
|
|
|
|
|
body ...+)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A named `let` form is equivalent to
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(letrec ([proc-id (lambda (arg-id ...)
|
|
|
|
|
body ...+)])
|
|
|
|
|
(proc-id init-expr ...))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
That is, a named `let` binds a function identifier that is visible only
|
|
|
|
|
in the function’s body, and it implicitly calls the function with the
|
|
|
|
|
values of some initial expressions.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(define (duplicate pos lst)
|
|
|
|
|
(let dup ([i 0]
|
|
|
|
|
[lst lst])
|
|
|
|
|
(cond
|
|
|
|
|
[(= i pos) (cons (car lst) lst)]
|
|
|
|
|
[else (cons (car lst) (dup (+ i 1) (cdr lst)))])))
|
|
|
|
|
|
|
|
|
|
> (duplicate 1 (list "apple" "cheese burger!" "banana"))
|
|
|
|
|
'("apple" "cheese burger!" "cheese burger!" "banana")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 5. Multiple Values: `let-values`, `let*-values`, `letrec-values`
|
|
|
|
|
|
|
|
|
|
> +\[missing\] in \[missing\] also documents multiple-value binding forms.
|
|
|
|
|
|
|
|
|
|
In the same way that `define-values` binds multiple results in a
|
|
|
|
|
definition \(see \[missing\]\), `let-values`, `let*-values`, and
|
|
|
|
|
`letrec-values` bind multiple results locally.
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(let-values ([(id ...) expr] ...)
|
|
|
|
|
body ...+)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(let*-values ([(id ...) expr] ...)
|
|
|
|
|
body ...+)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(letrec-values ([(id ...) expr] ...)
|
|
|
|
|
body ...+)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Each `expr` must produce as many values as corresponding `id`s. The
|
|
|
|
|
binding rules are the same for the forms without `-values` forms: the
|
|
|
|
|
`id`s of `let-values` are bound only in the `body`s, the `id`s of
|
|
|
|
|
`let*-values`s are bound in `expr`s of later clauses, and the `id`s of
|
|
|
|
|
`letrec-value`s are bound for all `expr`s.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (let-values ([(q r) (quotient/remainder 14 3)])
|
|
|
|
|
(list q r))
|
|
|
|
|
'(4 2)
|
|
|
|
|
```
|