doc work: set! in guide

svn: r6690
This commit is contained in:
Matthew Flatt 2007-06-19 00:23:11 +00:00
parent 38625c4980
commit e2dfc97726
6 changed files with 356 additions and 19 deletions

View File

@ -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))))))])))

View File

@ -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

View 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)
]

View File

@ -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]}

View 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)]

View File

@ -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