192 lines
5.9 KiB
Racket
192 lines
5.9 KiB
Racket
#lang scribble/doc
|
|
@(require scribble/manual
|
|
scribble/eval
|
|
"guide-utils.ss")
|
|
|
|
@title[#:tag "let"]{Local Binding}
|
|
|
|
Although internal @racket[define]s can be used for local binding,
|
|
Racket provides three forms that give the programmer more
|
|
control over bindings: @racket[let], @racket[let*], and
|
|
@racket[letrec].
|
|
|
|
@;------------------------------------------------------------------------
|
|
@section{Parallel Binding: @racket[let]}
|
|
|
|
@refalso["let"]{@racket[let]}
|
|
|
|
A @racket[let] form binds a set of identifiers, each to the result of
|
|
some expression, for use in the @racket[let] body:
|
|
|
|
@specform[(let ([id expr] ...) body ...+)]{}
|
|
|
|
The @racket[_id]s are bound ``in parallel.'' That is, no @racket[_id]
|
|
is bound in the right-hand side @racket[_expr] for any @racket[_id],
|
|
but all are available in the @racket[_body]. The @racket[_id]s must be
|
|
different from each other.
|
|
|
|
@examples[
|
|
(let ([me "Bob"])
|
|
me)
|
|
(let ([me "Bob"]
|
|
[myself "Robert"]
|
|
[I "Bobby"])
|
|
(list me myself I))
|
|
(let ([me "Bob"]
|
|
[me "Robert"])
|
|
me)
|
|
]
|
|
|
|
The fact that an @racket[_id]'s @racket[_expr] does not see its own
|
|
binding is often useful for wrappers that must refer back to the old
|
|
value:
|
|
|
|
@interaction[
|
|
(let ([+ (lambda (x y)
|
|
(if (string? x)
|
|
(string-append x y)
|
|
(+ x y)))]) (code:comment @#,t{use original @racket[+]})
|
|
(list (+ 1 2)
|
|
(+ "see" "saw")))
|
|
]
|
|
|
|
Occasionally, the parallel nature of @racket[let] bindings is
|
|
convenient for swapping or rearranging a set of bindings:
|
|
|
|
@interaction[
|
|
(let ([me "Tarzan"]
|
|
[you "Jane"])
|
|
(let ([me you]
|
|
[you me])
|
|
(list me you)))
|
|
]
|
|
|
|
The characterization of @racket[let] bindings as ``parallel'' is not
|
|
meant to imply concurrent evaluation. The @racket[_expr]s are
|
|
evaluated in order, even though the bindings are delayed until all
|
|
@racket[_expr]s are evaluated.
|
|
|
|
@;------------------------------------------------------------------------
|
|
@section{Sequential Binding: @racket[let*]}
|
|
|
|
@refalso["let"]{@racket[let*]}
|
|
|
|
The syntax of @racket[let*] is the same as @racket[let]:
|
|
|
|
@specform[(let* ([id expr] ...) body ...+)]{}
|
|
|
|
The difference is that each @racket[_id] is available for use in later
|
|
@racket[_expr]s, as well as in the @racket[_body]. Furthermore, the
|
|
@racket[_id]s need not be distinct, and the most recent binding is the
|
|
visible one.
|
|
|
|
@examples[
|
|
(let* ([x (list "Borroughs")]
|
|
[y (cons "Rice" x)]
|
|
[z (cons "Edgar" y)])
|
|
(list x y z))
|
|
(let* ([name (list "Borroughs")]
|
|
[name (cons "Rice" name)]
|
|
[name (cons "Edgar" name)])
|
|
name)
|
|
]
|
|
|
|
In other words, a @racket[let*] form is equivalent to nested
|
|
@racket[let] forms, each with a single binding:
|
|
|
|
@interaction[
|
|
(let ([name (list "Borroughs")])
|
|
(let ([name (cons "Rice" name)])
|
|
(let ([name (cons "Edgar" name)])
|
|
name)))
|
|
]
|
|
|
|
@;------------------------------------------------------------------------
|
|
@section{Recursive Binding: @racket[letrec]}
|
|
|
|
@refalso["let"]{@racket[letrec]}
|
|
|
|
The syntax of @racket[letrec] is also the same as @racket[let]:
|
|
|
|
@specform[(letrec ([id expr] ...) body ...+)]{}
|
|
|
|
While @racket[let] makes its bindings available only in the
|
|
@racket[_body]s, and @racket[let*] makes its bindings available to any
|
|
later binding @racket[_expr], @racket[letrec] makes its bindings
|
|
available to all other @racket[_expr]s---even earlier ones. In other
|
|
words, @racket[letrec] bindings are recursive.
|
|
|
|
The @racket[_expr]s in a @racket[letrec] form are most often
|
|
@racket[lambda] forms for recursive and mutually recursive functions:
|
|
|
|
@interaction[
|
|
(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)))
|
|
]
|
|
|
|
@interaction[
|
|
(letrec ([tarzan-in-tree?
|
|
(lambda (name path)
|
|
(or (equal? name "tarzan")
|
|
(and (directory-exists? path)
|
|
(tarzan-in-directory? path))))]
|
|
[tarzan-in-directory?
|
|
(lambda (dir)
|
|
(ormap (lambda (elem)
|
|
(tarzan-in-tree? (path-element->string elem)
|
|
(build-path dir elem)))
|
|
(directory-list dir)))])
|
|
(tarzan-in-tree? "tmp" (find-system-path 'temp-dir)))
|
|
]
|
|
|
|
While the @racket[_expr]s of a @racket[letrec] form are typically
|
|
@racket[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 @racket[_id]. If
|
|
an @racket[_id] is referenced before its value is ready, the result is
|
|
@|undefined-const|, as just as for internal definitions.
|
|
|
|
@interaction[
|
|
(letrec ([quicksand quicksand])
|
|
quicksand)
|
|
]
|
|
|
|
@; ----------------------------------------
|
|
@include-section["named-let.scrbl"]
|
|
|
|
@; ----------------------------------------
|
|
@section{Multiple Values: @racket[let-values], @racket[let*-values], @racket[letrec-values]}
|
|
|
|
@refalso["let"]{multiple-value binding forms}
|
|
|
|
In the same way that @racket[define-values] binds multiple
|
|
results in a definition (see @secref["multiple-values"]),
|
|
@racket[let-values], @racket[let*-values], and
|
|
@racket[letrec-values] bind multiple results locally.
|
|
|
|
@specform[(let-values ([(id ...) expr] ...)
|
|
body ...+)]
|
|
@specform[(let*-values ([(id ...) expr] ...)
|
|
body ...+)]
|
|
@specform[(letrec-values ([(id ...) expr] ...)
|
|
body ...+)]
|
|
|
|
Each @racket[_expr] must produce as many values as corresponding
|
|
@racket[_id]s. The binding rules are the same for the forms
|
|
without @racketkeywordfont{-values} forms: the @racket[_id]s of
|
|
@racket[let-values] are bound only in the @racket[_body]s, the
|
|
@racket[_id]s of @racket[let*-values]s are bound in
|
|
@racket[_expr]s of later clauses, and the @racket[_id]s of
|
|
@racket[letrec-value]s are bound for all @racket[_expr]s.
|
|
|
|
@examples[
|
|
(let-values ([(q r) (quotient/remainder 14 3)])
|
|
(list q r))
|
|
]
|