You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
typesetting/quad/qtest/mds/let.md

250 lines
8.6 KiB
Markdown

5 years ago
# 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 functions 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)
```