doc work: set! in guide
svn: r6690
This commit is contained in:
parent
38625c4980
commit
e2dfc97726
|
@ -16,7 +16,9 @@
|
|||
def+int
|
||||
defs+int
|
||||
examples
|
||||
examples*
|
||||
defexamples
|
||||
defexamples*
|
||||
as-examples
|
||||
|
||||
current-int-namespace
|
||||
|
@ -35,7 +37,7 @@
|
|||
(make-table
|
||||
#f
|
||||
(append
|
||||
(if title (list (list title)) null)
|
||||
(if title (list (list (make-flow (list title)))) null)
|
||||
(let loop ([expr-paras expr-paras]
|
||||
[val-list+outputs val-list+outputs]
|
||||
[first? #t])
|
||||
|
@ -270,19 +272,36 @@
|
|||
(interaction e ...)))]))
|
||||
|
||||
(define example-title
|
||||
(make-flow (list (make-paragraph (list "Examples:")))))
|
||||
(make-paragraph (list "Examples:")))
|
||||
(define-syntax examples
|
||||
(syntax-rules ()
|
||||
[(_ e ...)
|
||||
(titled-interaction example-title schemeinput* e ...)]))
|
||||
(define-syntax examples*
|
||||
(syntax-rules ()
|
||||
[(_ example-title e ...)
|
||||
(titled-interaction example-title schemeinput* e ...)]))
|
||||
(define-syntax defexamples
|
||||
(syntax-rules ()
|
||||
[(_ e ...)
|
||||
(titled-interaction example-title schemedefinput* e ...)]))
|
||||
(define-syntax defexamples*
|
||||
(syntax-rules ()
|
||||
[(_ example-title e ...)
|
||||
(titled-interaction example-title schemedefinput* e ...)]))
|
||||
|
||||
(define (as-examples t)
|
||||
(make-table #f
|
||||
(list
|
||||
(list example-title)
|
||||
(list (make-flow (list t)))))))
|
||||
(define (do-splice l)
|
||||
(cond
|
||||
[(null? l) null]
|
||||
[(splice? (car l)) (append (splice-run (car l))
|
||||
(do-splice (cdr l)))]
|
||||
[else (cons (car l) (do-splice (cdr l)))]))
|
||||
|
||||
(define as-examples
|
||||
(case-lambda
|
||||
[(t) (as-examples example-title t)]
|
||||
[(example-title t)
|
||||
(make-table #f
|
||||
(list
|
||||
(list (make-flow (list example-title)))
|
||||
(list (make-flow (do-splice (list t))))))])))
|
||||
|
|
|
@ -32,13 +32,13 @@
|
|||
|
||||
(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
|
||||
syntax quasisyntax unsyntax unsyntax-splicing
|
||||
set!)))
|
||||
(make-parameter '(require
|
||||
provide
|
||||
new send else => and or
|
||||
define-syntax syntax-rules define-struct
|
||||
quote quasiquote unquote unquote-splicing
|
||||
syntax quasisyntax unsyntax unsyntax-splicing
|
||||
set! set!-values)))
|
||||
(define current-variable-list
|
||||
(make-parameter null))
|
||||
(define current-meta-list
|
||||
|
|
130
collects/scribblings/guide/begin.scrbl
Normal file
130
collects/scribblings/guide/begin.scrbl
Normal file
|
@ -0,0 +1,130 @@
|
|||
#reader(lib "docreader.ss" "scribble")
|
||||
@require[(lib "manual.ss" "scribble")]
|
||||
@require[(lib "eval.ss" "scribble")]
|
||||
@require["guide-utils.ss"]
|
||||
|
||||
@title[#:tag "guide:begin"]{Sequencing}
|
||||
|
||||
Scheme programmers prefer to write programs with as few side-effects
|
||||
as possible, since purely functional code is more easily tested and
|
||||
composed into larger programs. Interaction with the external
|
||||
environment, however, requires sequencing, such as when writing to a
|
||||
display, opening a graphical window, or manipulating a file on disk.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Effects Before: @scheme[begin]}
|
||||
|
||||
A @scheme[begin] expression sequences expressions:
|
||||
|
||||
@specform[(begin expr ...+)]{}
|
||||
|
||||
The @scheme[_expr]s are evaluated in order, and the result of all but
|
||||
the last @scheme[_expr] is ignored. The result from the last
|
||||
@scheme[_expr] is the result of the @scheme[begin] form, and it is in
|
||||
tail position with respect to the @scheme[begin] form.
|
||||
|
||||
@defexamples[
|
||||
(define (print-triangle height)
|
||||
(if (zero? height)
|
||||
(void)
|
||||
(begin
|
||||
(display (make-string height #\*))
|
||||
(newline)
|
||||
(print-triangle (sub1 height)))))
|
||||
(print-triangle 4)
|
||||
]
|
||||
|
||||
Many forms, such as @scheme[lambda] or @scheme[cond] support a
|
||||
sequence of expressions even with a @scheme[begin]. Such positions are
|
||||
sometimes said to have an @defterm{implicit begin}.
|
||||
|
||||
@defexamples[
|
||||
(define (print-triangle height)
|
||||
(cond
|
||||
[(not (positive? height))
|
||||
(display (make-string height #\*))
|
||||
(newline)
|
||||
(print-triangle (sub1 height))]))
|
||||
(print-triangle 4)
|
||||
]
|
||||
|
||||
The @scheme[begin] form is special at the top level, at module level,
|
||||
or as a @scheme[body] after only internal definitions. In those
|
||||
positions, instead of forming an expression, the content of
|
||||
@scheme[begin] is spliced into the surrounding context.
|
||||
|
||||
@defexamples[
|
||||
(let ([curly 0])
|
||||
(begin
|
||||
(define moe (+ 1 curly))
|
||||
(define larry (+ 1 moe)))
|
||||
(list larry curly moe))
|
||||
]
|
||||
|
||||
This splicing behavior is mainly useful for macros, as we discuss
|
||||
later in @secref["guide:macros"].
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Effects After: @scheme[begin0]}
|
||||
|
||||
A @scheme[begin0] expression has the same syntax as a @scheme[begin]
|
||||
expression:
|
||||
|
||||
@specform[(begin0 expr ...+)]{}
|
||||
|
||||
The difference is that @scheme[begin0] returns the result of the first
|
||||
@scheme[expr], instead of the result of the last @scheme[expr]. The
|
||||
@scheme[begin0] form is useful for implementing side-effects that
|
||||
happen after a computation, especially in the case where the
|
||||
computation produces an unknown number of results.
|
||||
|
||||
@defexamples[
|
||||
(define (log-times thunk)
|
||||
(printf "Start: ~s\n" (current-inexact-milliseconds))
|
||||
(begin0
|
||||
(thunk)
|
||||
(printf "End..: ~s\n" (current-inexact-milliseconds))))
|
||||
(log-times (lambda () (sleep 0.1) 0))
|
||||
(log-times (lambda () (values 1 2)))
|
||||
]
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Effects If...: @scheme[when] and @scheme[unless]}
|
||||
|
||||
The @scheme[when] form combines an @scheme[if]-style conditional with
|
||||
sequencing for the ``then'' clause and no ``else'' clause:
|
||||
|
||||
@specform[(when test-expr then-expr ...)]
|
||||
|
||||
If @scheme[_test-expr] produces a true value, then all of the
|
||||
@scheme[_then-expr]s are evaluated. Otherwise, no @scheme[_then-expr]s
|
||||
are evaluated. The result is @|void-const| in any case.
|
||||
|
||||
The @scheme[unless] form is similar:
|
||||
|
||||
@specform[(unless test-expr then-expr ...)]
|
||||
|
||||
The difference is that the @scheme[_test-expr] result is inverted: the
|
||||
@scheme[_then-expr]s are evaluated only if the @scheme[_test-expr]
|
||||
result is @scheme[#f].
|
||||
|
||||
@defexamples[
|
||||
(define (enumerate lst)
|
||||
(if (null? (cdr lst))
|
||||
(printf "~a.\n" (car lst))
|
||||
(begin
|
||||
(printf "~a, " (car lst))
|
||||
(when (null? (cdr (cdr lst)))
|
||||
(printf "and "))
|
||||
(enumerate (cdr lst)))))
|
||||
(enumerate '("Larry" "Curly" "Moe"))
|
||||
]
|
||||
|
||||
@def+int[
|
||||
(define (print-triangle height)
|
||||
(unless (zero? height)
|
||||
(display (make-string height #\*))
|
||||
(newline)
|
||||
(print-triangle (sub1 height))))
|
||||
(print-triangle 4)
|
||||
]
|
|
@ -85,10 +85,8 @@ form, a @scheme[_thing] is either an identifier or a keyword.
|
|||
@include-section["define.scrbl"]
|
||||
@include-section["let.scrbl"]
|
||||
@include-section["cond.scrbl"]
|
||||
|
||||
@section[#:tag "guide:begin"]{Sequencing: @scheme[begin], @scheme[begin0], @scheme[when], and @scheme[unless]}
|
||||
|
||||
@section{Assignment: @scheme[set!]}
|
||||
@include-section["begin.scrbl"]
|
||||
@include-section["set.scrbl"]
|
||||
|
||||
@section{Quoted Data: @scheme[quote] and @scheme[quasiquote]}
|
||||
|
||||
|
|
190
collects/scribblings/guide/set.scrbl
Normal file
190
collects/scribblings/guide/set.scrbl
Normal file
|
@ -0,0 +1,190 @@
|
|||
#reader(lib "docreader.ss" "scribble")
|
||||
@require[(lib "manual.ss" "scribble")]
|
||||
@require[(lib "eval.ss" "scribble")]
|
||||
@require["guide-utils.ss"]
|
||||
|
||||
@interaction-eval[(require (lib "for.ss"))]
|
||||
|
||||
@title[#:tag "guide:set!"]{Assignment: @scheme[set!]}
|
||||
|
||||
Assign to a variable using @scheme[set!]:
|
||||
|
||||
@specform[(set! id expr)]
|
||||
|
||||
A @scheme[set!] expression evaluates @scheme[_expr] and changes
|
||||
@scheme[_id] (which must be bound in the enclosing environment) to the
|
||||
resulting value. The result of the @scheme[set!] expression itself is
|
||||
@|void-const|.
|
||||
|
||||
@defexamples[
|
||||
(define greeted null)
|
||||
(define (greet name)
|
||||
(set! greeted (cons name greeted))
|
||||
(string-append "Hello, " name))
|
||||
|
||||
(greet "Athos")
|
||||
(greet "Porthos")
|
||||
(greet "Aramis")
|
||||
greeted
|
||||
]
|
||||
|
||||
@defs+int[
|
||||
[(define (make-running-total)
|
||||
(let ([n 0])
|
||||
(lambda ()
|
||||
(set! n (+ n 1))
|
||||
n)))
|
||||
(define win (make-running-total))
|
||||
(define lose (make-running-total))]
|
||||
(win)
|
||||
(win)
|
||||
(lose)
|
||||
(win)
|
||||
]
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Guidelines for Using Assignment}
|
||||
|
||||
Although using @scheme[set!] is sometimes appropriate, Scheme style
|
||||
generally discourages the use of @scheme[set!]. The following
|
||||
guidelines may help explain when using @scheme[set!] is appropriate.
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{As in any modern language, assigning to shared identifier is no
|
||||
substitute for passing an argument to a procedure or getting
|
||||
its result.
|
||||
|
||||
@as-examples[@t{@bold{@italic{Really awful}} example:}
|
||||
@defs+int[
|
||||
[(define name "unknown")
|
||||
(define result "unknown")
|
||||
(define (greet)
|
||||
(set! result (string-append "Hello, " name)))]
|
||||
(set! name "John")
|
||||
(greet)
|
||||
result
|
||||
]]
|
||||
|
||||
@as-examples[@t{Ok example:}
|
||||
@def+int[
|
||||
(define (greet name)
|
||||
(string-append "Hello, " name))
|
||||
(greet "John")
|
||||
(greet "Anna")
|
||||
]]}
|
||||
|
||||
@item{A sequence of assignments to a local variable is far inferior
|
||||
to nested bindings.
|
||||
|
||||
@as-examples[@t{@bold{Bad} example:}
|
||||
@interaction[
|
||||
(let ([tree 0])
|
||||
(set! tree (list tree 1 tree))
|
||||
(set! tree (list tree 2 tree))
|
||||
(set! tree (list tree 3 tree))
|
||||
tree)]]
|
||||
|
||||
@as-examples[@t{Ok example:}
|
||||
@interaction[
|
||||
(let* ([tree 0]
|
||||
[tree (list tree 1 tree)]
|
||||
[tree (list tree 2 tree)]
|
||||
[tree (list tree 3 tree)])
|
||||
tree)]]}
|
||||
|
||||
@item{Using assignment to accumulate results from an iteration is
|
||||
bad style. Accumulating through a loop argument is better.
|
||||
|
||||
@as-examples[@t{Somewhat bad example:}
|
||||
@def+int[
|
||||
(define (sum lst)
|
||||
(let ([s 0])
|
||||
(for-each (lambda (i) (set! s (+ i s)))
|
||||
lst)
|
||||
s))
|
||||
(sum '(1 2 3))
|
||||
]]
|
||||
|
||||
@as-examples[@t{Ok example:}
|
||||
@def+int[
|
||||
(define (sum lst)
|
||||
(let loop ([lst lst][s 0])
|
||||
(if (null? lst)
|
||||
s
|
||||
(loop (cdr lst) (+ s (car lst))))))
|
||||
(sum '(1 2 3))
|
||||
]]
|
||||
|
||||
@as-examples[@t{Better (use an existing function) example:}
|
||||
@def+int[
|
||||
(define (sum lst)
|
||||
(apply + lst))
|
||||
(sum '(1 2 3))
|
||||
]]
|
||||
|
||||
@as-examples[@t{Good (a general approach) example:}
|
||||
@def+int[
|
||||
(define (sum lst)
|
||||
(for/fold ([s 0])
|
||||
([i (in-list lst)])
|
||||
(+ s i)))
|
||||
(sum '(1 2 3))
|
||||
]] }
|
||||
|
||||
@item{For cases where stateful objects are necessary or appropriate,
|
||||
then implementing the object's state with @scheme[set!] is
|
||||
fine.
|
||||
|
||||
@as-examples[@t{Ok example:}
|
||||
@def+int[
|
||||
(define next-number!
|
||||
(let ([n 0])
|
||||
(lambda ()
|
||||
(set! n (add1 n))
|
||||
n)))
|
||||
(next-number!)
|
||||
(next-number!)
|
||||
(next-number!)]]}
|
||||
|
||||
}
|
||||
|
||||
All else being equal, a program that uses no assignments or mutation
|
||||
is always preferable to one that uses assignments or mutation. While
|
||||
side effects are to be avoided, however, they should be used if the
|
||||
resulting code is significantly more readable or if it implements a
|
||||
significantly better algorithm.
|
||||
|
||||
The use of mutable values, such as vectors and hash tables, raises
|
||||
fewer suspicions about the style of a program that using @scheme[set!]
|
||||
directly. Nevertheless, simply replacing @scheme[set!]s in a program
|
||||
with a @scheme[vector-set!]s obviously does not improve the style of
|
||||
the program.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Multiple Values: @scheme[set!-values]}
|
||||
|
||||
The @scheme[set!-values] form assigns to multiple variables at once,
|
||||
given an expression that produces an appropriate number of values:
|
||||
|
||||
@specform[(set!-values (id ...) expr)]
|
||||
|
||||
This form is equivalent to using @scheme[let-values] to receive
|
||||
multiple results from @scheme[_expr], and then assign the results
|
||||
individually to the @scheme[_id]s using @scheme[set!].
|
||||
|
||||
@defexamples[
|
||||
(define game
|
||||
(let ([w 0]
|
||||
[l 0])
|
||||
(lambda (win?)
|
||||
(if win?
|
||||
(set! w (+ w 1))
|
||||
(set! l (+ l 1)))
|
||||
(begin0
|
||||
(values w l)
|
||||
(code:comment #, @t{swap sides...})
|
||||
(set!-values (w l) (values l w))))))
|
||||
(game #t)
|
||||
(game #t)
|
||||
(game #f)]
|
|
@ -116,7 +116,7 @@ position with respect to the original @scheme[or] form.
|
|||
@;------------------------------------------------------------------------
|
||||
@section{Guarded Evaluation: @scheme[when] and @scheme[unless]}
|
||||
|
||||
@defform[(while test-expr expr ...)]{
|
||||
@defform[(when test-expr expr ...)]{
|
||||
|
||||
Evaluates the @scheme[text-expr]. If the result is any value other
|
||||
than @scheme[#f], the @scheme[expr]s are evaluated, and the results
|
||||
|
|
Loading…
Reference in New Issue
Block a user