svn: r6685
This commit is contained in:
Matthew Flatt 2007-06-18 06:34:41 +00:00
parent eeaf779411
commit 1477c7ec1b
17 changed files with 763 additions and 99 deletions

View File

@ -215,12 +215,15 @@
(list " ")))
(define-syntax (schemedefinput* stx)
(syntax-case stx (eval-example-string define define-struct)
(syntax-case stx (eval-example-string define define-values define-struct)
[(_ (eval-example-string s))
#'(schemeinput* (eval-example-string s))]
[(_ (define . rest))
(syntax-case stx ()
[(_ e) #'(schemeblock+line e)])]
[(_ (define-values . rest))
(syntax-case stx ()
[(_ e) #'(schemeblock+line e)])]
[(_ (define-struct . rest))
(syntax-case stx ()
[(_ e) #'(schemeblock+line e)])]

View File

@ -213,7 +213,7 @@
(*defstruct 'name 'fields (lambda () (list desc ...)))]))
(define-syntax (defform*/subs stx)
(syntax-case stx ()
[(_ [spec spec1 ...] ([non-term-id non-term-form ...] ...) desc ...)
[(_ #:literals (lit ...) [spec spec1 ...] ([non-term-id non-term-form ...] ...) desc ...)
(with-syntax ([new-spec
(syntax-case #'spec ()
[(name . rest)
@ -224,7 +224,7 @@
#'name)
#'rest)
#'spec)])])
#'(*defforms #t
#'(*defforms #t '(lit ...)
'(spec spec1 ...)
(list (lambda (x) (schemeblock0 new-spec))
(lambda (ignored) (schemeblock0 spec1)) ...)
@ -233,7 +233,9 @@
(lambda () (schemeblock0 non-term-form))
...)
...)
(lambda () (list desc ...))))]))
(lambda () (list desc ...))))]
[(fm [spec spec1 ...] ([non-term-id non-term-form ...] ...) desc ...)
#'(fm #:literals () [spec spec1 ...] ([non-term-id non-term-form ...] ...) desc ...)]))
(define-syntax (defform* stx)
(syntax-case stx ()
[(_ [spec ...] desc ...) #'(defform*/subs [spec ...] () desc ...)]))
@ -242,41 +244,49 @@
[(_ spec desc ...) #'(defform*/subs [spec] () desc ...)]))
(define-syntax (defform/subs stx)
(syntax-case stx ()
[(_ #:literals lits spec subs desc ...) #'(defform*/subs #:literals lits [spec] subs desc ...)]
[(_ spec subs desc ...) #'(defform*/subs [spec] subs desc ...)]))
(define-syntax (defform/none stx)
(syntax-case stx ()
[(_ spec desc ...)
#'(*defforms #f
#'(*defforms #f null
'(spec) (list (lambda (ignored) (schemeblock0 spec)))
null null
(lambda () (list desc ...)))]))
(define-syntax specsubform
(syntax-rules ()
[(_ #:literals (lit ...) spec desc ...)
(*specsubform 'spec #f '(lit ...) (lambda () (schemeblock0 spec)) null null (lambda () (list desc ...)))]
[(_ spec desc ...)
(*specsubform 'spec #f (lambda () (schemeblock0 spec)) null null (lambda () (list desc ...)))]))
(*specsubform 'spec #f null (lambda () (schemeblock0 spec)) null null (lambda () (list desc ...)))]))
(define-syntax specspecsubform
(syntax-rules ()
[(_ spec desc ...)
(make-blockquote "leftindent" (list (specsubform spec desc ...)))]))
(define-syntax specform
(syntax-rules ()
[(_ #:literals (lit ...) spec desc ...)
(*specsubform 'spec #t '(lit ...) (lambda () (schemeblock0 spec)) null null (lambda () (list desc ...)))]
[(_ spec desc ...)
(*specsubform 'spec #t (lambda () (schemeblock0 spec)) null null (lambda () (list desc ...)))]))
(*specsubform 'spec #t null (lambda () (schemeblock0 spec)) null null (lambda () (list desc ...)))]))
(define-syntax specform/subs
(syntax-rules ()
[(_ spec ([non-term-id non-term-form ...] ...) desc ...)
[(_ #:literals (lit ...) spec ([non-term-id non-term-form ...] ...) desc ...)
(*specsubform 'spec #t
'(lit ...)
(lambda () (schemeblock0 spec))
'((non-term-id non-term-form ...) ...)
(list (list (lambda () (scheme non-term-id))
(lambda () (schemeblock0 non-term-form))
...)
...)
(lambda () (list desc ...)))]))
(lambda () (list desc ...)))]
[(_ spec ([non-term-id non-term-form ...] ...) desc ...)
(specform/subs #:literals () spec ([non-term-id non-term-form ...] ...) desc ...)]))
(define-syntax specsubform/inline
(syntax-rules ()
[(_ spec desc ...)
(*specsubform 'spec #f #f null null (lambda () (list desc ...)))]))
(*specsubform 'spec #f null #f null null (lambda () (list desc ...)))]))
(define-syntax defthing
(syntax-rules ()
[(_ id result desc ...)
@ -495,7 +505,7 @@
(define (meta-symbol? s) (memq s '(... ...+ ?)))
(define (*defforms kw? forms form-procs subs sub-procs content-thunk)
(define (*defforms kw? lits forms form-procs subs sub-procs content-thunk)
(parameterize ([current-variable-list
(apply
append
@ -503,7 +513,8 @@
(let loop ([form (cons (if kw? (cdr form) form)
subs)])
(cond
[(symbol? form) (if (meta-symbol? form)
[(symbol? form) (if (or (meta-symbol? form)
(memq form lits))
null
(list form))]
[(pair? form) (append (loop (car form))
@ -543,12 +554,13 @@
sub-procs))))
(content-thunk)))))
(define (*specsubform form has-kw? form-thunk subs sub-procs content-thunk)
(define (*specsubform form has-kw? lits form-thunk subs sub-procs content-thunk)
(parameterize ([current-variable-list
(append (let loop ([form (cons (if has-kw? (cdr form) form)
subs)])
(cond
[(symbol? form) (if (meta-symbol? form)
[(symbol? form) (if (or (meta-symbol? form)
(memq form lits))
null
(list form))]
[(pair? form) (append (loop (car form))

View File

@ -34,7 +34,7 @@
;; 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
new send if cond begin else => and or
define-syntax syntax-rules define-struct
quote quasiquote unquote unquote-splicing
syntax quasisyntax unsyntax unsyntax-splicing
@ -377,14 +377,14 @@
value-color]
[(identifier? c)
(cond
[is-var?
variable-color]
[(and (identifier? c)
(memq (syntax-e c) (current-keyword-list)))
keyword-color]
[(and (identifier? c)
(memq (syntax-e c) (current-meta-list)))
meta-color]
[is-var?
variable-color]
[it? variable-color]
[else symbol-color])]
[else paren-color])

View File

@ -20,7 +20,7 @@ bound as a syntax transformer (such as @scheme[if] or
A function call is evaluated by first evaluating the
@scheme[_proc-expr] and all @scheme[_arg-expr]s in order (left to
right). Then, if @scheme[_proc-expr] produced a function that accepts
right). Then, if @scheme[_proc-expr] produces a function that accepts
as many arguments as supplied @scheme[_arg-expr]s, the function is
called. Otherwise, an exception is raised.
@ -32,11 +32,11 @@ called. Otherwise, an exception is raised.
]
Some functions, such as @scheme[cons], accept a fixed number of
arguments. Some functions, such as @scheme[list], accept any number
of arguments. Some functions accept a range of argument counts; for
example @scheme[substring] accepts either two or three arguments. A
function's @idefterm{arity} is the number of arguments that it
accepts.
arguments. Some functions, such as @scheme[+] or @scheme[list], accept
any number of arguments. Some functions accept a range of argument
counts; for example @scheme[substring] accepts either two or three
arguments. A function's @idefterm{arity} is the number of arguments
that it accepts.
@;------------------------------------------------------------------------
@section[#:tag "guide:keyword-args"]{Keyword Arguments}

View File

@ -3,7 +3,7 @@
@require[(lib "eval.ss" "scribble")]
@require["guide-utils.ss"]
@title{Identifiers and Binding}
@title[#:tag "guide:binding"]{Identifiers and Binding}
The context of an expression determines the meaning of identifiers
that appear in the expression. In particular, starting a module with
@ -12,7 +12,7 @@ the language @schememodname[big], as in
@schememod[big]
means that, within the module, the identifiers described in this guide
have the the meaning described here: @scheme[cons] refers to the
start with the meaning described here: @scheme[cons] refers to the
function that creates a pair, @scheme[car] refers to the function
that extracts the first element of a pair, and so on.
@ -21,7 +21,7 @@ that extracts the first element of a pair, and so on.
Forms like @scheme[define], @scheme[lambda], and @scheme[let]
associate a meaning with one or more identifiers; that is, they
@defterm{bind} identifier. The part of the program for which the
@defterm{bind} identifiers. The part of the program for which the
binding applies is the @defterm{scope} of the binding. The set of
bindings in effect for a given expression is the expression's
@defterm{environment}.
@ -67,7 +67,7 @@ existing binding.
Even identifiers like @scheme[define] and @scheme[lambda] get their
meanings from bindings, though they have @defterm{transformer}
bindings (whcih means that they define syntactic forms) instead of
bindings (which means that they indicate syntactic forms) instead of
value bindings. Since @scheme[define] has a transformer binding, the
identifier @schemeidfont{define} cannot be used by itself to get a
value. However, the normal binding for @schemeidfont{define} can be

View File

@ -0,0 +1,163 @@
#reader(lib "docreader.ss" "scribble")
@require[(lib "manual.ss" "scribble")]
@require[(lib "eval.ss" "scribble")]
@require["guide-utils.ss"]
@title{Conditionals}
Most functions used for branching, such as @scheme[<] and
@scheme[string?], produce either @scheme[#t] or @scheme[#f]. Scheme's
branching forms, however, treat any value other than @scheme[#f] as
true. We we say a @defterm{true value} to mean any value other than
@scheme[#f].
This convention for ``true value'' meshes well with protocols where
@scheme[#f] can serve as failure or to indicate that an optional value
is not supplied. (Beware of overusing this trick, and remember that an
exception is usually a better mechanism to report failure.)
For example, the @scheme[member] function serves double duty; it can
be used to find the tail of a list that starts with a particular item,
or it can be used to simply check whether an item is present in a
list:
@interaction[
(member "Groucho" '("Harpo" "Zeppo"))
(member "Groucho" '("Harpo" "Groucho" "Zeppo"))
(if (member "Groucho" '("Harpo" "Zeppo"))
'yep
'nope)
(if (member "Groucho" '("Harpo" "Groucho" "Zeppo"))
'yep
'nope)
]
@;------------------------------------------------------------------------
@section{Simple Branching: @scheme[if]}
In an @scheme[if] form,
@specform[(if test-expr then-expr else-expr)]
the @scheme[_test-expr] is always evaluated. If it produces any value
other than @scheme[#f], then @scheme[_then-expr] is
evaluated. Otherwise, @scheme[_else-expr] is evaluated.
An @scheme[if] form must have both an @scheme[_then-expr] and an
@scheme[_else-expr]; the latter is not optional. To perform (or skip)
side-effects based on a @scheme[_test-expr], use @scheme[when] or
@scheme[unless], which we describe later in @secref["guide:begin"].
@;------------------------------------------------------------------------
@section{Combining Tests: @scheme[and] and @scheme[or]}
Scheme's @scheme[and] and @scheme[or] are syntactic forms, rather than
functions. Unlike a function, the @scheme[and] and @scheme[or] forms
can skip evaluation of later expressions if an earlier one determines
the answer.
@specform[(and expr ...)]
An @scheme[or] form produces @scheme[#f] if any of its @scheme[expr]s
produces @scheme[#f]. Otherwise, it produces the value of its last
@scheme[_expr]. As a special case, @scheme[(and)] produces
@scheme[#t].
@specform[(or expr ...)]
The @scheme[and] form produces @scheme[#f] if any of its
@scheme[_expr]s produces @scheme[#f]. Otherwise, it produces the first
non-@scheme[#f] value from its @scheme[expr]s. As a special case,
@scheme[(or)] produces @scheme[#f].
@examples[
(code:line
(define (got-milk? lst)
(and (not (null? lst))
(or (eq? 'milk (car lst))
(got-milk? (cdr lst))))) (code:comment #, @t{recurs only if needed}))
(got-milk? '(apple banana))
(got-milk? '(apple milk banana))
]
If evaluation reaches the last @scheme[_expr] of an @scheme[and] or
@scheme[or] form, then the @scheme[_expr]'s value directly determines
the @scheme[and] or @scheme[or] result. Therefore, the last
@scheme[expr] is in tail position, which means that the above
@scheme[got-milk?] function runs in constant space.
@margin-note{For an introduction to tail calls and tail positions, see
@secref["guide:tail-recursion"].}
@;------------------------------------------------------------------------
@section{Chaining Tests: @scheme[cond]}
The @scheme[cond] form chains a series of tests to select a result
expression. To a first approximation, the syntax of @scheme[cond] is
as follows:
@specform[(cond [test-expr expr ...+]
...)]
Each @scheme[_test-expr] is evaluated in order. If it produces
@scheme[#f], the corresponding @scheme[_expr]s are ignored, and
evaluation proceeds to the next @scheme[_test-expr]. As soon as a
@scheme[_test-expr] produces a true value, its @scheme[_text-expr]s
are evaluated to produce the result for the @scheme[cond] form, and no
further @scheme[_test-expr]s are evaluated.
The last @scheme[_test-expr] in a @scheme[cond] can be replaced by
@scheme[else]. In terms of evaluation, @scheme[else] serves as a
synonym for @scheme[#t], but it clarifies that the last clause is
meant to catch all remaining cases. If @scheme[else] is not used, then
it is possible that no @scheme[_test-expr]s produce a true value; in
that case, the result of the @scheme[cond] expression is
@|void-const|.
@examples[
(cond
[(= 2 3) (error "wrong!")]
[(= 2 2) 'ok])
(cond
[(= 2 3) (error "wrong!")])
(cond
[(= 2 3) (error "wrong!")]
[else 'ok])
]
@def+int[
(define (got-milk? lst)
(cond
[(null? lst) #f]
[(eq? 'milk (car lst)) #t]
[else (got-milk? (cdr lst))]))
(got-milk? '(apple banana))
(got-milk? '(apple milk banana))
]
The full syntax of @scheme[cond] includes two more kinds of clauses:
@specform/subs[#:literals (else =>)
(cond cond-clause ...)
([cond-clause [test-expr then-expr ...+]
[else then-expr ...+]
[test-expr => proc-expr]
[test-expr]])]
The @scheme[=>] variant captures the true result of its
@scheme[_test-expr] and passes it to the result of the
@scheme[_proc-expr], which must be a function of one argument.
@examples[
(define (after-groucho lst)
(cond
[(member "Groucho" lst) => cdr]
[else (error "not there")]))
(after-groucho '("Harpo" "Groucho" "Zeppo"))
(after-groucho '("Harpo" "Zeppo"))
]
A clause that includes only a @scheme[_test-expr] is rarely used. It
captures the true result of the @scheme[_test-expr], and simply
returns the result for the whole @scheme[cond] expression.

View File

@ -30,7 +30,7 @@ definitions:
which is a shorthand for
@schemeblock[
(define id (lambda (arg ...) _body ...+))
(define _id (lambda (_arg ...) _body ...+))
]
@defexamples[
@ -51,12 +51,12 @@ The function shorthand via @scheme[define] also supports a ``rest''
argument (i.e., a final argument to collect extra arguments in a
list):
@specform[(define (id arg ... . rest-id) expr)]{}
@specform[(define (id arg ... . rest-id) body ...+)]{}
which is a shorthand
@schemeblock[
(define id (lambda (id arg ... . rest-id) body-expr ...+))
(define _id (lambda (_arg ... . _rest-id) _body ...+))
]
@defexamples[
@ -69,22 +69,25 @@ which is a shorthand
@section{Curried Function Shorthand}
Consider the following @scheme[make-add-suffix] function that takes a
string and returns another function that takes a string. Normally, the
result of @scheme[make-add-suffix] would be sent to some other
function, but it could be called directly, like this:
string and returns another function that takes a string:
@def+int[
(define make-add-suffix
(lambda (s2)
(lambda (s) (string-append s s2))))
]
Although it's not common, result of @scheme[make-add-suffix] could be
called directly, like this:
@interaction[
((make-add-suffix "!") "hello")
]
In a sense, the @scheme[make-add-suffix] function takes two arguments,
but it takes them one at a time. A function that takes some of its
arguments and returns a function to consume more is sometimes called a
@defterm{curried function}.
In a sense, @scheme[make-add-suffix] is a function takes two
arguments, but it takes them one at a time. A function that takes some
of its arguments and returns a function to consume more is sometimes
called a @defterm{curried function}.
Using the function-shorthand form of @scheme[define],
@scheme[make-add-suffix] can be written equivalently as
@ -236,7 +239,7 @@ the result is a special value @|undefined-const|.
A sequence of internal definitions using just @scheme[define] is
easily translated to an equivalent @scheme[letrec] form (as introduced
in the next section). However, other definition forms can appear as a
@scheme[_body], including @scheme[define-struct] (see
@scheme[_body], including @scheme[define-values], @scheme[define-struct] (see
@secref["guide:define-struct"]) or @scheme[define-syntax] (see
@secref["guide:macros"]).

View File

@ -48,7 +48,7 @@ convention implicitly defines the meaning of many meta-variables:
Square brackets in the grammar indicate a parenthesized sequence of
forms, where square brackets are normally used (by convention). That
is square brackets @italic{do not} mean optional parts of the
is, square brackets @italic{do not} mean optional parts of the
syntactic form.
A @schememetafont{...} indicates zero or more repetitions of the
@ -56,7 +56,7 @@ preceding form, and @schememetafont{...+} indicates one or more
repetitions of the preceding datum. Otherwise, non-italicized
identifiers stand form themselves.
Based on the above grammar, then, here are a few legal uses of
Based on the above grammar, then, here are a few conforming uses of
@schemekeywordfont{something}:
@schemeblock[
@ -65,9 +65,10 @@ Based on the above grammar, then, here are a few legal uses of
(#,(schemekeywordfont "something") [x my-favorite-martian x] (+ 1 2) #f)
]
Some syntactic forms refer to meta-variables that are not implicitly
defined and not previously defined. Such meta-variables are defined
using a BNF-like format for alternatives:
Some syntactic-form specifications refer to meta-variables that are
not implicitly defined and not previously defined. Such meta-variables
are defined after the main form, using a BNF-like format for
alternatives:
@specform/subs[(#,(schemekeywordfont "something-else") [thing ...+] an-expr ...)
([thing thing-id
@ -83,11 +84,9 @@ form, a @scheme[_thing] is either an identifier or a keyword.
@include-section["lambda.scrbl"]
@include-section["define.scrbl"]
@include-section["let.scrbl"]
@include-section["named-let.scrbl"]
@include-section["cond.scrbl"]
@section{Conditionals: @scheme[if], @scheme[cond], @scheme[and], and @scheme[or]}
@section{Sequencing: @scheme[begin], @scheme[begin0], @scheme[when], and @scheme[unless]}
@section[#:tag "guide:begin"]{Sequencing: @scheme[begin], @scheme[begin0], @scheme[when], and @scheme[unless]}
@section{Assignment: @scheme[set!]}

View File

@ -99,6 +99,13 @@ using @idefterm{contracts}.
@; ----------------------------------------------------------------------
@section[#:tag "performance"]{Performance}
Every definition or expression is compiled to an internal bytecode
format. Standard optimizations are applied when compiling the
bytecode. For example, in an environment where when @scheme[+] has its
usual binding, the expression @scheme[(let ([x 1][y (lambda () 4)]) (+
1 (y)))] is compiled the same as the constant @scheme[5] due to
constant propagation, constant folding, and inlining optimizations.
@; ----------------------------------------------------------------------
@section[#:tag "ffi"]{Foreign-Function Interface@aux-elem{ (FFI)}}

View File

@ -5,7 +5,7 @@
@title[#:tag "guide:lambda"]{Functions@aux-elem{ (Procedures)}: @scheme[lambda]}
Such a @scheme[lambda] expression creates a function. In the simplest
A @scheme[lambda] expression creates a function. In the simplest
case, a @scheme[lambda] expression has the form
@specform[
@ -180,11 +180,11 @@ specifies a keyword-based argument with a default value.
The @scheme[lambda] form does not directly support the creation
of a function that accepts ``rest'' keywords. To construct a
function that accepts any and all keyword arguments, use
function that accepts all keyword arguments, use
@scheme[make-keyword-procedure]. The function supplied to
@scheme[make-keyword-procedure] receives keyword arguments through
parallel lists in the first two (by-position) arguments, and
then all by-position arguments from an application as the
@scheme[make-keyword-procedure] receives keyword arguments
through parallel lists in the first two (by-position) arguments,
and then all by-position arguments from an application as the
remaining by-position arguments.
@margin-note{For an introduction to @scheme[keyword-apply], see

View File

@ -3,5 +3,183 @@
@require[(lib "eval.ss" "scribble")]
@require["guide-utils.ss"]
@title{Local Binding: @scheme[let], @scheme[let*], and @scheme[letrec]}
@interaction-eval[(require (lib "file.ss"))]
@title{Local Binding}
Although internal @scheme[define]s can be used for local binding,
Scheme provides three forms that give the programmer more
control over bindings: @scheme[let], @scheme[let*], and
@scheme[letrec].
@;------------------------------------------------------------------------
@section{Parallel Binding: @scheme[let]}
A @scheme[let] form binds a set of identifiers, each to the result of
some expression, for use in the @scheme[let] body:
@specform[(let ([id expr] ...) body ...+)]{}
The @scheme[_id]s are bound ``in parallel.'' That is, no @scheme[_id]
is bound in the right-hand side @scheme[_expr] for any @scheme[_id],
but all are available in the @scheme[_body]. The @scheme[_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 @scheme[_id]'s @scheme[_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 @scheme[+]})
(list (+ 1 2)
(+ "see" "saw")))
]
Occasionally, the parallel nature of @scheme[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 @scheme[let] bindings as ``parallel'' is not
meant to imply concurrent evaluation. The @scheme[_expr]s are
evaluated in order, even though the bindings are delayed until all
@scheme[_expr]s are evaluated.
@;------------------------------------------------------------------------
@section{Sequential Binding: @scheme[let*]}
The syntax of @scheme[let*] is the same as @scheme[let]:
@specform[(let* ([id expr] ...) body ...+)]{}
The difference is that each @scheme[_id] is available for use in later
@scheme[_expr]s, as well as in the @scheme[_body]. Furthermore, the
@scheme[_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 @scheme[let*] form is equivalent to nested
@scheme[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: @scheme[letrec]}
The syntax of @scheme[letrec] is also the same as @scheme[let]:
@specform[(letrec ([id expr] ...) body ...+)]{}
While @scheme[let] makes its bindings available only in the
@scheme[_body]s, and @scheme[let*] makes its bindings available to any
later binding @scheme[_expr], @scheme[letrec] makes its bindings
available to all other @scheme[_expr]s---even earlier ones. In other
words, @scheme[letrec] bindings are recursive.
The @scheme[_expr]s in a @scheme[letrec] form are most often
@scheme[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 @scheme[_expr]s of a @scheme[letrec] form are typically
@scheme[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 @scheme[_id]. If
an @scheme[_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: @scheme[let-values], @scheme[let*-values], and @scheme[letrec-values]}
In the same way that @scheme[define-values] binds multiple
results in a definition (see @secref["guide:multiple-values"]),
@scheme[let-values], @scheme[let*-values], and
@scheme[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 @scheme[_expr] must produce as many values as coresponding
@scheme[_id]s. The binding rules are the same for the forms
without @schemekeywordfont{-values} forms: the @scheme[_id]s of
@scheme[let-values] are bound only in the @scheme[_body]s, the
@scheme[_id]s of @scheme[let*-values]s are bound in
@scheme[_expr]s of later clauses, and the @scheme[_id]s of
@scheme[letrec-value]s are bound for all @scheme[_expr]s.
@examples[
(let-values ([(q r) (quotient/remainder 14 3)])
(list q r))
]

View File

@ -182,6 +182,9 @@ If the derivation of the above definitions is mysterious to you,
consider reading @|HtDP|. If you are merely suspicious of the use
of recursive calls instead of a looping construct, then read on.
@;------------------------------------------------------------------------
@section[#:tag "guide:tail-recursion"]{Tail Recursion}
Both the @scheme[my-length] and @scheme[my-map] functions run in
@math{O(n)} time for a list of length @math{n}. This is easy to see by
imagining how @scheme[(my-length (list "a" "b" "c"))] must evaluate:
@ -238,7 +241,10 @@ one, because that takes up space for no good reason.
This evaluation behavior is sometimes called @idefterm{tail-call
optimization}, but it's not merely an ``optimization'' in Scheme; it's
a guarantee about the way the code will run.
a guarantee about the way the code will run. More precisely, an
expression in @idefterm{tail position} with respect to another
expression does not take extra computation space over the other
expression.
In the case of @scheme[my-map], @math{O(n)} space complexity is
reasonable, since it has to generate a result of size

View File

@ -10,18 +10,16 @@ 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[
@specform[
(let _proc-id ([_arg-id _init-expr] ...)
_body-expr ...+)
_body ...+)
]
is equivalent to
A named @scheme[let] form is equivalent to
@schemeblock[
(letrec ([_proc-id (lambda (_arg-id ...)
_body-expr ...+)])
_body ...+)])
(_proc-id _init-expr ...))
]

View File

@ -0,0 +1,31 @@
#reader(lib "docreader.ss" "scribble")
@require[(lib "manual.ss" "scribble")]
@require[(lib "eval.ss" "scribble")]
@require["guide-utils.ss"]
@title[#:tag "ports"]{Input and Output Ports}
A @defterm{port} encapsulates an I/O stream, normally for just one
direction. An @defterm{input port} reads from a stream, and an
@defterm{output port} writes to a string.
For many procedures that accept a port argument, the argument is
optional, and it defaults to either the @defterm{current input port}
or @defterm{current output port}. For @exec{mzscheme}, the current
ports are initialized to the process's stdin and stdout. The
@scheme[current-input-port] and @scheme[current-output-port]
procedures, whien called with no arguments, return the current output
and input port, respectively.
@examples[
(display "hello world\n")
(display "hello world\n" (current-output-port))
]
Ports are created by various procedures that specific to the different
kinds of streams. For example, @scheme[open-input-file] creates an
input port for reading from a file. Procedures like
@scheme[with-input-from-file] both create a port and install it as the
current port while calling a given body procedure.
See @secref["io"] for information about using ports.

View File

@ -5,4 +5,143 @@
@local-table-of-contents[]
@;------------------------------------------------------------------------
@section[#:tag "mz:cond"]{Conditionals: @scheme[cond]}
@defform/subs[#:literals (else =>)
(cond cond-clause ...)
([cond-clause [test-expr then-expr ...+]
[else then-expr ...+]
[test-expr => proc-expr]
[test-expr]])]{
A @scheme[cond-clause] that starts with @scheme[else] must be the last
@scheme[cond-clause].
If no @scheme[cond-clause]s are present, the result is @|void-const|.
If only a @scheme[[else then-expr ...+]] is present, then the
@scheme[then-expr]s are evaluated. The results from all but the last
@scheme[then-expr] are ignored. The results of the last
@scheme[then-expr], which is in tail position with respect to the
@scheme[cond] form, provides the result for the whole @scheme[cond]
form.
Otherwise, the first @scheme[test-expr] is evaluated. If it produces
@scheme[#f], then the result is the same as a @scheme[cond] form with
the remaining @scheme[cond-clause]s, in tail position with respect to
the original @scheme[cond] form. Otherwise, evaluation depends on the
form of the @scheme[cond-clause]:
@specsubform[[test-expr then-expr ...+]]{The @scheme[then-expr]s are
evaluated in order, and the results from all but the last
@scheme[then-expr] are ignored. The results of the last
@scheme[then-expr], which is in tail position with respect to the
@scheme[cond] form, provides the result for the whole @scheme[cond]
form.}
@specsubform[#:literals (=>) [test-expr => proc-expr]]{The @scheme[proc-expr] is
evaluated, and it must produce a procedure that accepts on argument,
otherwise the @exnraise[exn:fail:contract]. The procedure is applied
to the result of @scheme[test-expr] in tail position with respect to
the @scheme[cond] expression.}
@specsubform[[test-expr]]{The result of the @scheme[test-expr] is
returned as the result of the @scheme[cond] form. The
@scheme[test-expr] is not in tail position.}
@examples[
(cond)
(cond
[else 5])
(cond
[(positive? -5) (error "doesn't get here")]
[(zero? -5) (error "doesn't get here, either")]
[(positive? 5) 'here])
(cond
[(member 2 '(1 2 3)) => (lambda (l) (map - l))])
(cond
[(member 2 '(1 2 3))])
]}
@;------------------------------------------------------------------------
@section{Boolean Combination: @scheme[and] and @scheme[or]}
@defform[(and expr ...)]{
If no @scheme[expr]s are provided, then result is @scheme[#f].
If a single @scheme[expr] is provided, then it is in tail position, so
the results of the @scheme[and] expression are the results of the
@scheme[expr].
Otherwise, the first @scheme[expr] is evaluated. If it produces
@scheme[#f], the result of the @scheme[and] expression is
@scheme[#f]. Otherwise, the result is the same as an @scheme[and]
expression with the remaining @scheme[expr]s in tail position with
respect to the original @scheme[and] form.
@examples[
(and)
(and 1)
(and (values 1 2))
(and #f (error "doesn't get here"))
(and #t 5)
]}
@defform[(or expr ...)]{
If no @scheme[expr]s are provided, then result is @scheme[#t].
If a single @scheme[expr] is provided, then it is in tail position, so
the results of the @scheme[and] expression are the results of the
@scheme[expr].
Otherwise, the first @scheme[expr] is evaluated. If it produces a
value other than @scheme[#f], that result is the result of the
@scheme[or] expression. Otherwise, the result is the same as an
@scheme[or] expression with the remaining @scheme[expr]s in tail
position with respect to the original @scheme[or] form.
@examples[
(or)
(or 1)
(or (values 1 2))
(or 5 (error "doesn't get here"))
(or #f 5)
]}
@;------------------------------------------------------------------------
@section{Guarded Evaluation: @scheme[when] and @scheme[unless]}
@defform[(while 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
are ignored. No @scheme[expr] is in tail position with respect to the
@scheme[when] form.
@examples[
(when (positive? -5)
(display "hi"))
(when (positive? 5)
(display "hi")
(display " there"))
]}
@defform[(unless test-expr expr ...)]{
Equivalent to @scheme[(when (not test-expr) expr ...)].
@examples[
(unless (positive? 5)
(display "hi"))
(unless (positive? -5)
(display "hi")
(display " there"))
]}
@;------------------------------------------------------------------------
@include-section["for.scrbl"]

View File

@ -69,7 +69,7 @@ application of @scheme[-] cannot be reduced until the sub-expression
@scheme[(+ 1 1)] is reduced.
Thus, the specification of each syntactic form specifies how (some of)
it's sub-expressions are evaluated, and then how the results are
its sub-expressions are evaluated, and then how the results are
combined to reduce the form away.
The @defterm{dynamic extent} of an expression is the sequence of
@ -177,7 +177,7 @@ right-hand expression must be reduced to a value.
[{}
(begin (code:hilite (define x 10)) (+ x 1))]
[{(define x 10)}
(code:hilite (begin #,void-const (+ x 1)))]
(code:hilite (begin (void) (+ x 1)))]
[{(define x 10)}
(+ (code:hilite x) 1)]
[{(define x 10)}
@ -197,7 +197,7 @@ existing top-level binding:
[{(define x 10)}
(begin (code:hilite (set! x 8)) x)]
[{(define x 8)}
(code:hilite (begin #,void-const x))]
(code:hilite (begin (void) x))]
[{(define x 8)}
(code:hilite x)]
[{(define x 8)}
@ -217,7 +217,7 @@ values, which are the results of expressions, and @defterm{objects},
which hold the data referenced by a value.
A few kinds of objects can serve directly as values, including
booleans, @|void-const|, and small exact integers. More generally,
booleans, @scheme[(void)], and small exact integers. More generally,
however, a value is a reference to an object. For example, a value can
be a reference to a particular vector that currently holds the value
@scheme[10] in its first slot. If an object is modified, then the
@ -235,58 +235,58 @@ create objects, such as @scheme[vector], add to the set of objects:
(define y x)
(vector-set! x 0 11)
(vector-ref y 0))]
[{(define <o1> #(10 20))}
[{(define <o1> (vector 10 20))}
{}
(begin (code:hilite (define x <o1>))
(define y x)
(vector-set! x 0 11)
(vector-ref y 0))]
[{(define <o1> #(10 20))}
[{(define <o1> (vector 10 20))}
{(define x <o1>)}
(code:hilite (begin #,void-const
(code:hilite (begin (void)
(define y x)
(vector-set! x 0 11)
(vector-ref y 0)))]
[{(define <o1> #(10 20))}
[{(define <o1> (vector 10 20))}
{(define x <o1>)}
(begin (define y (code:hilite x))
(vector-set! x 0 11)
(vector-ref y 0))]
[{(define <o1> #(10 20))}
[{(define <o1> (vector 10 20))}
{(define x <o1>)}
(begin (code:hilite (define y <o1>))
(vector-set! x 0 11)
(vector-ref y 0))]
[{(define <o1> #(10 20))}
[{(define <o1> (vector 10 20))}
{(define x <o1>)
(define y <o1>)}
(code:hilite (begin #,void-const
(code:hilite (begin (void)
(vector-set! x 0 11)
(vector-ref y 0)))]
[{(define <o1> #(10 20))}
[{(define <o1> (vector 10 20))}
{(define x <o1>)
(define y <o1>)}
(begin (vector-set! (code:hilite x) 0 11)
(vector-ref y 0))]
[{(define <o1> #(10 20))}
[{(define <o1> (vector 10 20))}
{(define x <o1>)
(define y <o1>)}
(begin (code:hilite (vector-set! <o1> 0 11))
(vector-ref y 0))]
[{(define <o1> #(11 20))}
[{(define <o1> (vector 11 20))}
{(define x <o1>)
(define y <o1>)}
(code:hilite (begin #,void-const
(code:hilite (begin (void)
(vector-ref y 0)))]
[{(define <o1> #(11 20))}
[{(define <o1> (vector 11 20))}
{(define x <o1>)
(define y <o1>)}
(vector-ref (code:hilite y) 0)]
[{(define <o1> #(11 20))}
[{(define <o1> (vector 11 20))}
{(define x <o1>)
(define y <o1>)}
(code:hilite (vector-ref <o1> 0))]
[{(define <o1> #(11 20))}
[{(define <o1> (vector 11 20))}
{(define x <o1>)
(define y <o1>)}
11]
@ -334,8 +334,8 @@ specified with the datatype and its associated procedures.
In the program state
@prog-steps[
[{(define <o1> #(10 20))
(define <o2> #(0))}
[{(define <o1> (vector 10 20))
(define <o2> (vector 0))}
{(define x <o1>)}
(+ 1 x)]
]
@ -432,7 +432,7 @@ the variable is always replaced with a location by the time the
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
{(define f <p1>)
(define xloc 3)}
(code:hilite (begin #,void-const xloc))]
(code:hilite (begin (void) xloc))]
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
{(define f <p1>)
(define xloc 3)}
@ -463,7 +463,7 @@ in an initial program refer to variables. A top-level binding is both
a variable and a location. Any other variable is always replaced by a
location at run-time, so that evaluation of expressions involves only
locations. A single non-top-level variable, such as a procedure
argument, can correspond to multiple locations at different times.
argument, can correspond to different locations at different times.
The replacement of a variable with a location during evaluation
implements Scheme's @defterm{lexical scoping}. For example, when the
@ -473,12 +473,14 @@ procedure, including with any nested @scheme[lambda] forms. As a
result, future references of the variable always access the same
location.
@guideintro["guide:binding"]{binding}
An @defterm{identifier} is source-program entity. Parsing a Scheme
program reveals that some identifiers correspond to variables, some
refer to syntactic forms, and some are quoted to produce a symbol or a
syntax object. An identifier @scheme[binds] another when the former is
syntax object. An identifier @defterm{binds} another when the former is
parsed as a variable and the latter is parsed as a reference to the
former. An identifier is @scheme[bound] in a sub-expression if it
former. An identifier is @defterm{bound} in a sub-expression if it
binds any uses of the identifier in the sub-expression that are not
otherwise bound within the sub-expression; conversely, a binding for a
sub-expression @defterm{shadows} any bindings in its context, so that

View File

@ -1,6 +1,8 @@
#reader(lib "docreader.ss" "scribble")
@require["mz.ss"]
@define[cvt (schemefont "CVT")]
@title[#:tag "mz:syntax" #:style 'toc]{Core Syntactic Forms}
This section describes core syntax forms that apear in a fully
@ -89,7 +91,7 @@ references are disallowed anywhere within a @scheme[module] form.
@defform/none[(proc-expr arg ...)]{
Applies a procedure, normally, when @scheme[proc-expr] is not an
Applies a procedure, when @scheme[proc-expr] is not an
identifier that has a transformer binding (see
@secref["mz:expansion"]).
@ -126,8 +128,8 @@ in the application, then the procedure is called with the values of
the @scheme[arg-expr]s. Otherwise, the @exnraise[exn:fail:contract].
The continuation of the procedure call is the same as the continuation
of the application expression, so the result(s) of the application
expression is(are) the result(s) of the procedure.
of the application expression, so the results of the procedure are the
results of the application expression.
The relative order of @scheme[_keyword]-based arguments matters only
for the order of @scheme[_arg-expr] evaluations; the arguments are
@ -162,7 +164,7 @@ according to their order in the application form.
Produces a procedure. The @scheme[gen-formals] determines the number of
arguments that the procedure accepts. It is either a simple
@scheme[formals], or one of the extended forms.
@scheme[formals] or one of the extended forms.
A simple @scheme[_formals] has one of the following three forms:
@ -239,12 +241,9 @@ When multiple identifiers appear in a @scheme[gen-formals], they must be
distinct according to @scheme[bound-identifier=?].
If the procedure procedure by @scheme[lambda] is applied to fewer or
more arguments than it accepts, the @exnraise[exn:fail:contract]. If
@scheme[gen-formals] includes @scheme[keyword]s and an application
includes too few arguments before the keyword section, the same
keyword in multiple positions, or a keyword that is not among the
@scheme[gen-formals] @scheme[_keyword]s, then the
@exnraise[exn:fail:contract].
more by-position or arguments than it accepts, to by-keyword arguments
that it does not accept, or without required by-keyword arguments, then
the @exnraise[exn:fail:contract].
The last @scheme[body] expression is in tail position with respect to
the procedure body.
@ -322,9 +321,9 @@ within the @scheme[body]s to the procedure itself.}
Similar to @scheme[let], but evaluates the @scheme[val-expr]s one by
one, creating a location for each @scheme[id] as soon as the value is
availablek. The @scheme[id]s are bound in the remaining @scheme[val-expr]s
available. The @scheme[id]s are bound in the remaining @scheme[val-expr]s
as well as the @scheme[body]s, and the @scheme[id]s need not be
distinct.
distinct; later bindings shadow earlier bindings.
@examples[
(let ([x 1]
@ -352,8 +351,9 @@ created first and filled with @|undefined-const|, and all
@defform[(let-values ([(id ...) val-expr] ...) body ...+)]{ Like
@scheme[let], except that each @scheme[val-expr] must produce as many
values as corresponding @scheme[id]s. A separate location is created
for each @scheme[id], all of which are bound in the @scheme[body]s.
values as corresponding @scheme[id]s, otherwise the
@exnraise[exn:fail:contract]. A separate location is created for each
@scheme[id], all of which are bound in the @scheme[body]s.
@examples[
(let-values ([(x y) (quotient/remainder 10 3)])
@ -392,8 +392,131 @@ and in the @scheme[body]s.
]}
@;------------------------------------------------------------------------
@section{Sequencing: @scheme[begin]}
@section[#:tag "mz:if"]{Conditionals: @scheme[if]}
@defform[(if test-expr then-expr else-expr)]{
Evaluates @scheme[test-expr]. If it produces any value other than
@scheme[#f], then @scheme[then-expr] is evaluated, and its results are
the result for the @scheme[if] form. Otherwise, @scheme[else-expr] is
evaluated, and its results are the result for the @scheme[if]
form. The @scheme[then-expr] and @scheme[else-expr] are in tail
position with respect to the @scheme[if] form.
@examples[
(if (positive? -5) (error "doesn't get here") 2)
(if (positive? 5) 1 (error "doesn't get here"))
]}
@;------------------------------------------------------------------------
@section[#:tag "mz:define"]{Definitions: @scheme[define] and @scheme[define-values]}
@defform*/subs[[(define id expr)
(define (head args) body ...+)]
([head id
(head args)]
[args (code:line arg ...)
(code:line arg ... #, @schemeparenfont{.} rest-id)]
[arg arg-id
[arg-id default-expr]
(code:line keyword arg-id)
(code:line keyword [arg-id default-expr])])]{
The first form binds @scheme[id] to the result of @scheme[expr], and
the second form binds @scheme[id] to a procedure. In the second case,
the generation procedure is @scheme[(#,cvt (head args) body ...+)],
using the @|cvt| meta-function defined as follows:
@schemeblock[
(#,cvt (id . _gen-formals) . _datum) = (lambda _gen-formals . _datum)
(#,cvt (head . _gen-formals) . _datum) = (lambda _gen-formals expr)
#, @elem{if} (#,cvt head . _datum) = expr
]
At the top level, the top-level binding @scheme[id] is created after
evaluating @scheme[expr], if it does not exist already, and the
top-level mapping of @scheme[id] (see @secref["mz:namespace"]) is set
to the binding at the same time.
@defexamples[
(define x 10)
x
]
@def+int[
(define (f x)
(+ x 1))
(f 10)
]
@def+int[
(define ((f x) [y 20])
(+ x y))
((f 10) 30)
((f 10))
]
}
@defform[(define-values (id ...) expr)]{
Evaluates the @scheme[expr], and binds the results to the
@scheme[id]s, in order, if the number of results matches the number of
@scheme[id]s; if @scheme[expr] produces a different number of results,
the @exnraise[exn:fail:contract].
At the top level, the top-level binding for each @scheme[id] is
created after evaluating @scheme[expr], if it does not exist already,
and the top-level mapping of each @scheme[id] (see
@secref["mz:namespace"]) is set to the binding at the same time.
@defexamples[
(define-values () (values))
(define-values (x y z) (values 1 2 3))
z
]
}
@;------------------------------------------------------------------------
@section{Sequencing: @scheme[begin] and @scheme[begin0]}
@defform*[[(begin form ...)
(begin expr ...+)]]{
The first form applies when @scheme[begin] appears at the top level,
at module level, or in an internal-definition position (before any
expression in the internal-definition sequence). In that case, the
@scheme[begin] form is equivalent to splicing the @scheme[form]s into
the enclosing context.
The second form applies for @scheme[begin] in an expression position.
In that case, the @scheme[expr]s are evaluated in order, and the
results are ignored for all but the last @scheme[expr]. The last
@scheme[expr] is in tail position with respect to the @scheme[begin]
form.
@examples[
(begin
(define x 10)
x)
(+ 1 (begin
(printf "hi\n")
2))
(let-values ([(x y) (begin
(values 1 2 3)
(values 1 2))])
(list x y))
]}
@defform[(begin0 expr body ...+)]{
Evaluates the @scheme[expr], then evaluates the @scheme[body]s,
ignoring the @scheme[body] results. The results of the @scheme[expr]
are the results of the @scheme[begin0] form, but the @scheme[expr] is
in tail position only if no @scheme[body]s are present.
@examples[
(begin0
(values 1 2)
(printf "hi\n"))
]}