guide edits, including demotion of for/list
svn: r6499
This commit is contained in:
parent
3856d9e6a4
commit
a2e6a837ae
|
@ -29,7 +29,9 @@
|
|||
(define opt-color "schemeopt")
|
||||
|
||||
(define current-keyword-list
|
||||
;; This is temporary, until the MzScheme manual is filled in...
|
||||
(make-parameter '(define require provide
|
||||
define-values begin0 when unless
|
||||
new send if cond begin else and or
|
||||
define-syntax syntax-rules define-struct
|
||||
quote quasiquote unquote unquote-splicing
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@require[(lib "eval.ss" "scribble")]
|
||||
@require["guide-utils.ss"]
|
||||
|
||||
@title{Definitions}
|
||||
@title{Definitions: @scheme[define] and @scheme[define-values]}
|
||||
|
||||
A definition can have the form
|
||||
|
||||
|
|
|
@ -291,7 +291,7 @@ the same facility with nested iterations:
|
|||
(list book chapter))
|
||||
]
|
||||
|
||||
@section{@scheme[for/fold] and @scheme[for*/fold]}
|
||||
@section[#:tag "guide:for/fold"]{@scheme[for/fold] and @scheme[for*/fold]}
|
||||
|
||||
The @scheme[for/fold] form generalizes the way to combine iteration
|
||||
results. Its syntax is slightly different than the syntax of
|
||||
|
|
|
@ -16,5 +16,14 @@ complete coverage of the basic Scheme syntactic forms.
|
|||
@include-section["apply.scrbl"]
|
||||
@include-section["lambda.scrbl"]
|
||||
@include-section["define.scrbl"]
|
||||
@include-section["for.scrbl"]
|
||||
@include-section["let.scrbl"]
|
||||
@include-section["named-let.scrbl"]
|
||||
|
||||
@section{Conditionals: @scheme[if], @scheme[cond], @scheme[and], and @scheme[or]}
|
||||
|
||||
@section{Sequencing: @scheme[begin], @scheme[begin0], @scheme[when], and @scheme[unless]}
|
||||
|
||||
@section{Assignment: @scheme[set!]}
|
||||
|
||||
@section{Quoted Data: @scheme[quote] and @scheme[quasiquote]}
|
||||
|
||||
|
|
|
@ -37,6 +37,10 @@ In the reference manual, the documentation for each procedure
|
|||
describes the acceptable arguments and the result of the procedure
|
||||
using @idefterm{contracts}.
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
@include-section["for.scrbl"]
|
||||
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
@section[#:tag "classes"]{Classes and Objects}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@require[(lib "eval.ss" "scribble")]
|
||||
@require["guide-utils.ss"]
|
||||
|
||||
@title[#:tag "guide:lambda"]{Procedures}
|
||||
@title[#:tag "guide:lambda"]{Procedures: @scheme[lambda] and @scheme[case-lambda]}
|
||||
|
||||
A @scheme[lambda] expression creates a procedure. In the simplest
|
||||
case, a @scheme[lambda] expression has the form
|
||||
|
|
7
collects/scribblings/guide/let.scrbl
Normal file
7
collects/scribblings/guide/let.scrbl
Normal file
|
@ -0,0 +1,7 @@
|
|||
#reader(lib "docreader.ss" "scribble")
|
||||
@require[(lib "manual.ss" "scribble")]
|
||||
@require[(lib "eval.ss" "scribble")]
|
||||
@require["guide-utils.ss"]
|
||||
|
||||
@title{Local Binding: @scheme[let], @scheme[let*], and @scheme[letrec]}
|
||||
|
|
@ -107,95 +107,16 @@ procedures. One reason is that @scheme[map], @scheme[ormap],
|
|||
@scheme[andmap], and @scheme[filter] cover the most common kinds of
|
||||
list loops.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Iterative Folds and Comprehensions: @scheme[for/fold] and @scheme[for/list]}
|
||||
|
||||
Besides iteration procedures like @scheme[foldl], Scheme provides a
|
||||
syntactic form for iteration that more closely resembles the syntax of
|
||||
other languages. The @scheme[foldl] example above can be written with
|
||||
the @scheme[for/fold] syntax as follows:
|
||||
|
||||
@interaction[
|
||||
(for/fold ([sum 0])
|
||||
([elem (list 1 2 3)])
|
||||
(+ sum (* elem elem)))
|
||||
]
|
||||
|
||||
Compare to analogous Java code, where @scheme[(list 1 2 3)] is
|
||||
replaced by a collection @scheme[lst]:
|
||||
|
||||
@verbatim[
|
||||
#<<EOS
|
||||
int sum = 0;
|
||||
for (Object elem : lst) {
|
||||
sum = sum + elem * elem;
|
||||
}
|
||||
return sum;
|
||||
EOS
|
||||
]
|
||||
|
||||
The only significant difference is that the updating of @scheme[sum]
|
||||
and the return of @scheme[sum]'s value are implicit. Those implicit
|
||||
actions are why the form is called @scheme[for/fold] instead of just
|
||||
@scheme[for].
|
||||
|
||||
Along similar lines, the @scheme[for/list] form iterates through a list
|
||||
and implicitly accumulates each result into a list:
|
||||
|
||||
@interaction[
|
||||
(for/list ([i (list "peanuts" "popcorn" "crackerjack")])
|
||||
(string-append i "!"))
|
||||
]
|
||||
|
||||
The @scheme[for/list] form is a @defterm{list compherension} form, as
|
||||
in Haskell, Ruby, Python, and other languages. One advantage over
|
||||
@scheme[map] is that it can iterate over more things than just lists.
|
||||
For example, @scheme[for/list] can iterate over a range of numbers:
|
||||
|
||||
@interaction[
|
||||
(for/list ([i (in-range 0 10)])
|
||||
i)
|
||||
]
|
||||
|
||||
The @scheme[for/list] form can even iterate over a list and a range of
|
||||
numbers in parallel:
|
||||
|
||||
@interaction[
|
||||
(for/list ([s (list "a" "b" "c")]
|
||||
[n (in-range 0 3)])
|
||||
(if (= n 2)
|
||||
"oops!"
|
||||
s))
|
||||
]
|
||||
|
||||
Note that the binding syntax of @scheme[for/fold] and
|
||||
@scheme[for/list] is similar to that of @scheme[let] (as introduced in
|
||||
@secref["local-binding-intro"]). In the same way that @scheme[let*]
|
||||
supports nested bindings, @scheme[for*/list] supports nested
|
||||
iterations:
|
||||
|
||||
@interaction[
|
||||
(for*/list ([s (list "a" "b" "c")]
|
||||
[n (list "x" "y" "z")])
|
||||
(string-append s n))
|
||||
]
|
||||
|
||||
Unlike the @scheme[for/list], the nested iteration of
|
||||
@scheme[for*/list] covers patterns with lists not as easily expressed
|
||||
with @scheme[map]. When procedures like @scheme[map] suffice, however,
|
||||
Scheme programmers tend to use them, partly because the syntax is
|
||||
simpler (just a procedure call).
|
||||
|
||||
We have ignored several other variants of the interation
|
||||
form---including plain @scheme[for], which is used when the iteration
|
||||
body is to be run only for its effect. For more complete information,
|
||||
see @secref["guide:for"].
|
||||
Scheme provides a general @defterm{list compherension} form
|
||||
@scheme[for/list], which builds a list by iterating through
|
||||
@defterm{sequences}. List comprehensions and related iteration forms
|
||||
are described in see @secref["guide:for"].
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{List Iteration from Scratch}
|
||||
|
||||
Although @scheme[map] and @scheme[for/list] are predefined, they are
|
||||
not primitive in any interesting sense. You can write equivalent
|
||||
Although @scheme[map] and other iteration procedures predefined, they
|
||||
are not primitive in any interesting sense. You can write equivalent
|
||||
iterations using a handful of list primitives.
|
||||
|
||||
Since a Scheme list is a linked list, the two core operations on a
|
||||
|
@ -406,74 +327,3 @@ directly, so the tail-call ``optimization'' kicks in:
|
|||
#,step (cons "a" (list "b"))
|
||||
#,step (list "a" "b")
|
||||
]
|
||||
|
||||
Tail-call behavior becomes even more important when dealing with
|
||||
non-list data or when using an object-oriented style. In the latter
|
||||
case, an object must sometimes dispatch to another object; if the
|
||||
other object's result is the complete answer, there's no reason for
|
||||
the first object to wait around. We defer futher discussion of this
|
||||
point until @secref["datatypes"], after which we'll have more forms of
|
||||
data to consider.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Named @scheme[let]}
|
||||
|
||||
As you start reading Scheme code, you'll discover one more form that
|
||||
is commonly used to implement iterations and recursive functions:
|
||||
@idefterm{named @scheme[let]}. A named @scheme[let] uses the same
|
||||
syntactic keyword as a simple sequence of local bindings, but an
|
||||
@nonterm{id} after the @scheme[let] (instead of an immediate
|
||||
open parenthesis) triggers a different parsing. In general,
|
||||
|
||||
@schemeblock[
|
||||
#, @BNF-seq[@litchar{(} @litchar{let} @nonterm{proc-id} @litchar{(}
|
||||
@kleenestar{@BNF-group[@litchar{[} @nonterm{arg-id} @nonterm{init-expr} @litchar{]}]}
|
||||
@litchar{)}
|
||||
@kleeneplus{@nonterm{body-expr}} @litchar{)}]
|
||||
]
|
||||
|
||||
is equivalent to the sequence
|
||||
|
||||
@schemeblock[
|
||||
#, @BNF-seq[@litchar{(}@litchar{define} @litchar{(} @nonterm{proc-id} @kleenestar{@nonterm{arg-id}} @litchar{)}
|
||||
@kleeneplus{@nonterm{body-expr}} @litchar{)}]
|
||||
#, @BNF-seq[@litchar{(}@nonterm{proc-id} @kleenestar{@nonterm{init-expr}}@litchar{)}]
|
||||
]
|
||||
|
||||
except that the @scheme[let] form works in any expression
|
||||
context.
|
||||
|
||||
That is, a named @scheme[let] binds a procedure identifier that is
|
||||
visible only in the procedure's body, and it implicitly calls the
|
||||
procedure with the values of some initial expressions. A named
|
||||
@scheme[let] looks similar to the start of @scheme[for/fold], but the
|
||||
recursive calls in the body are explicit, and they are not constrained
|
||||
to tail position.
|
||||
|
||||
As an example, here is @scheme[my-map] once again, using a named let
|
||||
to bind the local @scheme[iter] procedure:
|
||||
|
||||
@schemeblock[
|
||||
(define (my-map f lst)
|
||||
(let iter ([lst lst]
|
||||
[backward-result empty])
|
||||
(cond
|
||||
[(empty? lst) (reverse backward-result)]
|
||||
[else (iter (rest lst)
|
||||
(cons (f (first lst))
|
||||
backward-result))])))
|
||||
]
|
||||
|
||||
Here's another example, where the local @scheme[dup] procedure is used
|
||||
recursively and not merely iteratively, and where the traversal of a
|
||||
list stops part-way:
|
||||
|
||||
@def+int[
|
||||
(define (duplicate pos lst)
|
||||
(let dup ([i 0]
|
||||
[lst lst])
|
||||
(cond
|
||||
[(= i pos) (cons (first lst) lst)]
|
||||
[else (cons (first lst) (dup (+ i 1) (rest lst)))])))
|
||||
(duplicate 1 (list "apple" "cheese burger!" "banana"))
|
||||
]
|
||||
|
|
41
collects/scribblings/guide/named-let.scrbl
Normal file
41
collects/scribblings/guide/named-let.scrbl
Normal file
|
@ -0,0 +1,41 @@
|
|||
#reader(lib "docreader.ss" "scribble")
|
||||
@require[(lib "manual.ss" "scribble")]
|
||||
@require[(lib "eval.ss" "scribble")]
|
||||
@require["guide-utils.ss"]
|
||||
|
||||
@title{Named @scheme[let]}
|
||||
|
||||
A named @scheme[let] is an iteration and recursion form. It uses the
|
||||
same syntactic keyword @scheme[let] as for local binding, but an
|
||||
identifier after the @scheme[let] (instead of an immediate open
|
||||
parenthesis) triggers a different parsing.
|
||||
|
||||
In general,
|
||||
|
||||
@schemeblock[
|
||||
(let _proc-id ([_arg-id _init-expr] ...)
|
||||
_body-expr ...+)
|
||||
]
|
||||
|
||||
is equivalent to
|
||||
|
||||
@schemeblock[
|
||||
(letrec ([_proc-id (lambda (_arg-id ...)
|
||||
_body-expr ...+)])
|
||||
(_proc-id _init-expr ...))
|
||||
]
|
||||
|
||||
That is, a named @scheme[let] binds a procedure identifier that is
|
||||
visible only in the procedure's body, and it implicitly calls the
|
||||
procedure with the values of some initial expressions.
|
||||
|
||||
@defexamples[
|
||||
(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"))
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user