
[It will not bother me if we revert this commit. I liked SK's idea and found it easy to implement. I wonder if others will be worried that it is easy to unintentionally leave off the second argument to check-error. I also wonder if it is problematic to add new string constants, like I've done.] Here is an example: (check-error (/ 1 0) "/: division by zero") (check-error (/ 1 0) "divide by zero") (check-error (/ 1 0)) (check-error 1) Here is the output: Ran 4 tests. 2 of the 4 tests failed. No signature violations. Check failures: check-error encountered the following error instead of the expected divide by zero :: /: division by zero in ex.rkt, line 2, column 0 check-error expected an error, but instead received the value 1. in ex.rkt, line 4, column 0
366 lines
13 KiB
Racket
366 lines
13 KiB
Racket
#lang scribble/doc
|
|
@(require "common.ss"
|
|
"std-grammar.ss"
|
|
"prim-ops.ss"
|
|
(for-label lang/htdp-beginner))
|
|
|
|
|
|
@title[#:style 'toc #:tag "beginner"]{Beginning Student}
|
|
|
|
@declare-exporting[lang/htdp-beginner]
|
|
|
|
@schemegrammar*+library[
|
|
#:literals (define define-struct lambda cond else if and or empty true false require lib planet
|
|
check-expect check-within check-error)
|
|
(check-expect check-within check-error require)
|
|
[program (code:line def-or-expr ...)]
|
|
[def-or-expr definition
|
|
expr
|
|
test-case
|
|
library-require]
|
|
[definition (define (id id id ...) expr)
|
|
(define id expr)
|
|
(define id (lambda (id id ...) expr))
|
|
(define-struct id (id ...))]
|
|
[expr (code:line (id expr expr ...) (code:comment @#,seclink["beginner-call"]{function call}))
|
|
(code:line (prim-op expr ...) (code:comment @#,seclink["beginner-prim-call"]{primitive operation call}))
|
|
(cond [expr expr] ... [expr expr])
|
|
(cond [expr expr] ... [else expr])
|
|
(if expr expr expr)
|
|
(and expr expr expr ...)
|
|
(or expr expr expr ...)
|
|
empty
|
|
id
|
|
(code:line id (code:comment @#,seclink["beginner-id"]{identifier}))
|
|
(code:line @#,elem{@schemevalfont{'}@scheme[id]} (code:comment @#,seclink["beginner-quote"]{symbol}))
|
|
number
|
|
true
|
|
false
|
|
string
|
|
character]
|
|
]
|
|
|
|
@|prim-nonterms|
|
|
|
|
@prim-ops['(lib "htdp-beginner.ss" "lang") #'here]
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[define]}
|
|
|
|
@defform[(define (id id id ...) expr)]{
|
|
|
|
Defines a function. The first @scheme[id] inside the parentheses is
|
|
the name of the function. All remaining @scheme[id]s are the names of
|
|
the function's arguments. The @scheme[expr] is the body of the
|
|
function, evaluated whenever the function is called. The name of the
|
|
function cannot be that of a primitive or another definition.}
|
|
|
|
@defform/none[#:literals (define)
|
|
(define id expr)]{
|
|
|
|
Defines a constant @scheme[id] as a synonym for the value produced by
|
|
@scheme[expr]. The defined name cannot be that of a primitive or
|
|
another definition, and @scheme[id] itself must not appear in
|
|
@scheme[expr].}
|
|
|
|
|
|
@defform/none[#:literals (define lambda)
|
|
(define id (lambda (id id ...) expr))]{
|
|
|
|
An alternate form for defining functions. The first @scheme[id] is the
|
|
name of the function. The @scheme[id]s in parentheses are the names of
|
|
the function's arguments, and the @scheme[expr] is the body of the
|
|
function, which evaluated whenever the function is called. The name
|
|
of the function cannot be that of a primitive or another definition.}
|
|
|
|
@defidform[lambda]{
|
|
|
|
The @scheme[lambda] keyword can only be used with @scheme[define] in
|
|
the alternative function-definition syntax.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[define-struct]}
|
|
|
|
@defform[(define-struct structid (fieldid ...))]{
|
|
|
|
Define a new type of structure. The structure's fields are named by
|
|
the @scheme[fieldid]s in parentheses. After evaluation of a
|
|
define-struct form, a set of new primitives is available for creation,
|
|
extraction, and type-like queries:
|
|
|
|
@itemize[
|
|
|
|
@item{@schemeidfont{make-}@scheme[structid] : takes a number of
|
|
arguments equal to the number of fields in the structure type,
|
|
and creates a new instance of the structure type.}
|
|
|
|
@item{@scheme[structid]@schemeidfont{-}@scheme[fieldid] : takes an
|
|
instance of the structure and returns the field named by
|
|
@scheme[structid].}
|
|
|
|
@item{@scheme[structid]@schemeidfont{?} : takes any value, and returns
|
|
@scheme[true] if the value is an instance of the structure type.}
|
|
|
|
@item{@scheme[structid] : an identifier representing the structure
|
|
type, but never used directly.}
|
|
|
|
]
|
|
|
|
The created names must not be the same as a primitive or another defined name.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section[#:tag "beginner-call"]{Function Calls}
|
|
|
|
@defform/none[(id expr expr ...)]{
|
|
|
|
Calls a function. The @scheme[id] must refer to a defined function,
|
|
and the @scheme[expr]s are evaluated from left to right to produce the
|
|
values that are passed as arguments to the function. The result of the
|
|
function call is the result of evaluating the function's body with
|
|
every instance of an argument name replaced by the value passed for
|
|
that argument. The number of argument @scheme[expr]s must be the same
|
|
as the number of arguments expected by the function.}
|
|
|
|
@defform[(#%app id expr expr ...)]{
|
|
|
|
A function call can be written with @scheme[#%app], though it's
|
|
practically never written that way.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section[#:tag "beginner-prim-call"]{Primitive Calls}
|
|
|
|
@defform/none[(prim-op expr ...)]{
|
|
|
|
Like a @seclink["beginner-call"]{function call}, but for a primitive
|
|
operation. The @scheme[expr]s are evaluated from left to right, and
|
|
passed as arguments to the primitive operation named by
|
|
@scheme[prim-op]. A @scheme[define-struct] form creates new
|
|
primitives.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[cond]}
|
|
|
|
@defform[(cond [expr expr] ... [expr expr])]{
|
|
|
|
A @scheme[cond] form contains one or more ``lines'' that are
|
|
surrounded by parentheses or square brackets. Each line contains two
|
|
@scheme[expr]s: a question @scheme[expr] and an answer
|
|
@scheme[expr].
|
|
|
|
The lines are considered in order. To evaluate a line, first evaluate
|
|
the question @scheme[expr]. If the result is @scheme[true], then the
|
|
result of the whole @scheme[cond] expression is the result of
|
|
evaluating the answer @scheme[expr] of the same line. If the result of
|
|
evaluating the question @scheme[expr] is @scheme[false], the line is
|
|
discarded and evaluation proceeds with the next line.
|
|
|
|
If the result of a question @scheme[expr] is neither @scheme[true] nor
|
|
@scheme[false], it is an error. If none of the question @scheme[expr]s
|
|
evaluates to @scheme[true], it is also an error.}
|
|
|
|
@defform/none[#:literals (cond else)
|
|
(cond [expr expr] ... [else expr])]{
|
|
|
|
This form of @scheme[cond] is similar to the prior one, except that
|
|
the final @scheme[else] clause is always taken if no prior line's test
|
|
expression evaluates to @scheme[true]. In other words, @scheme[else]
|
|
acts like @scheme[true], so there is no possibility to ``fall off the
|
|
end'' of the @scheme[cond] form.}
|
|
|
|
@defidform[else]{
|
|
|
|
The @scheme[else] keyword can be used only with @scheme[cond].}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[if]}
|
|
|
|
@defform[(if expr expr expr)]{
|
|
|
|
The first @scheme[expr] (known as the ``test'' @scheme[expr]) is
|
|
evaluated. If it evaluates to @scheme[true], the result of the
|
|
@scheme[if] expression is the result of evaluating the second
|
|
@scheme[expr] (often called the ``then'' @scheme[expr]). If the text
|
|
@scheme[expr] evaluates to @scheme[false], the result of the
|
|
@scheme[if] expression is the result of evaluating the third
|
|
@scheme[expr] (known as the ``else'' @scheme[expr]). If the
|
|
result of evaluating the test @scheme[expr] is neither @scheme[true]
|
|
nor @scheme[false], it is an error.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[and]}
|
|
|
|
@defform[(and expr expr expr ...)]{
|
|
|
|
The @scheme[expr]s are evaluated from left to right. If the first
|
|
@scheme[expr] evaluates to @scheme[false], the @scheme[and] expression
|
|
immediately evaluates to @scheme[false]. If the first @scheme[expr]
|
|
evaluates to @scheme[true], the next expression is considered. If all
|
|
@scheme[expr]s evaluate to @scheme[true], the @scheme[and] expression
|
|
evaluates to @scheme[true]. If any of the expressions evaluate to a
|
|
value other than @scheme[true] or @scheme[false], it is an error.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[or]}
|
|
|
|
@defform[(or expr expr expr ...)]{
|
|
|
|
The @scheme[expr]s are evaluated from left to right. If the first
|
|
@scheme[expr] evaluates to @scheme[true], the @scheme[or] expression
|
|
immediately evaluates to @scheme[true]. If the first @scheme[expr]
|
|
evaluates to @scheme[false], the next expression is considered. If all
|
|
@scheme[expr]s evaluate to @scheme[false], the @scheme[or] expression
|
|
evaluates to @scheme[false]. If any of the expressions evaluate to a
|
|
value other than @scheme[true] or @scheme[false], it is an error.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{Test Cases}
|
|
|
|
@defform[(check-expect expr expr)]{
|
|
|
|
A test case to check that the first @scheme[expr] produces the same
|
|
value as the second @scheme[expr], where the latter is normally an
|
|
immediate value.}
|
|
|
|
@defform[(check-within expr expr expr)]{
|
|
|
|
Like @scheme[check-expect], but with an extra expression that produces
|
|
a number @scheme[_delta]. The test case checks that each number in the
|
|
result of the first @scheme[expr] is within @scheme[_delta] of each
|
|
corresponding number from the second @scheme[expr].}
|
|
|
|
@defform*[[(check-error expr expr)
|
|
(check-error expr)]]{
|
|
|
|
A test case to check that the first @scheme[expr] signals an error,
|
|
where the error messages matches the string produced by the second
|
|
@scheme[expr], if it is present.}
|
|
|
|
@defform[(check-member-of expr expr expr ...)]{
|
|
|
|
A test case to check that the first @scheme[expr] produces an element
|
|
that is equivalent to one of the following @scheme[expr]s.}
|
|
|
|
@defform[(check-range expr expr expr)]{
|
|
|
|
A test case to check that the first @scheme[expr] produces a number
|
|
inbetween the numbers produced by the second and third @scheme[expr]s,
|
|
inclusive.}
|
|
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[empty]}
|
|
|
|
@defthing[empty empty?]{
|
|
|
|
The empty list.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section[#:tag "beginner-id"]{Identifiers}
|
|
|
|
@defform/none[id]{
|
|
|
|
An @scheme[id] refers to a defined constant or argument within a
|
|
function body. If no definition or argument matches the @scheme[id]
|
|
name, an error is reported. Similarly, if @scheme[id] matches the name
|
|
of a defined function or primitive operation, an error is reported.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section[#:tag "beginner-quote"]{Symbols}
|
|
|
|
@deftogether[(
|
|
@defform/none[(unsyntax @elem{@schemevalfont{'}@scheme[id]})]
|
|
@defform[(quote id)]
|
|
)]{
|
|
|
|
A quoted @scheme[id] is a symbol. A symbol is a constant, like
|
|
@scheme[0] and @scheme[empty].
|
|
|
|
Normally, a symbol is written with a @litchar{'}, like
|
|
@scheme['apple], but it can also be written with @scheme[quote], like
|
|
@scheme[(@#,scheme[quote] apple)].
|
|
|
|
The @scheme[id] for a symbol is a sequence of characters not including
|
|
a space or one of the following:}
|
|
|
|
@t{@hspace[2] @litchar{"} @litchar{,} @litchar{'} @litchar{`}
|
|
@litchar{(} @litchar{)} @litchar{[} @litchar{]}
|
|
@litchar["{"] @litchar["}"] @litchar{|} @litchar{;}
|
|
@litchar{#}}
|
|
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[true] and @scheme[false]}
|
|
|
|
@defthing[true boolean?]{
|
|
|
|
The true value.}
|
|
|
|
@defthing[false boolean?]{
|
|
|
|
The false value.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[require]}
|
|
|
|
@defform[(require string)]{
|
|
|
|
Makes the definitions of the module specified by @scheme[string]
|
|
available in the current module (i.e., current file), where @scheme[string]
|
|
refers to a file relative to the enclosing file.
|
|
|
|
The @scheme[string] is constrained in several ways to avoid problems
|
|
with different path conventions on different platforms: a @litchar{/}
|
|
is a directory separator, @litchar{.} always means the current
|
|
directory, @litchar{..} always means the parent directory, path
|
|
elements can use only @litchar{a} through @litchar{z} (uppercase or
|
|
lowercase), @litchar{0} through @litchar{9}, @litchar{-}, @litchar{_},
|
|
and @litchar{.}, and the string cannot be empty or contain a leading
|
|
or trailing @litchar{/}.}
|
|
|
|
@defform/none[#:literals (require)
|
|
(require module-id)]{
|
|
|
|
Accesses a file in an installed library. The library name is an
|
|
identifier with the same constraints as for a relative-path string,
|
|
with the additional constraint that it must not contain a
|
|
@litchar{.}.}
|
|
|
|
@defform/none[#:literals (require lib)
|
|
(require (lib string string ...))]{
|
|
|
|
Accesses a file in an installed library, making its definitions
|
|
available in the current module (i.e., current file). The first
|
|
@scheme[string] names the library file, and the remaining
|
|
@scheme[string]s name the collection (and sub-collection, and so on)
|
|
where the file is installed. Each string is constrained in the same
|
|
way as for the @scheme[(require string)] form.}
|
|
|
|
|
|
@defform/none[#:literals (require planet)
|
|
(require (planet string (string string number number)))]{
|
|
|
|
|
|
Accesses a library that is distributed on the internet via the PLaneT
|
|
server, making it definitions available in the current module (i.e.,
|
|
current file).}
|
|
|
|
@; ----------------------------------------
|
|
|
|
@section[#:tag "beginner-prim-ops"]{Primitive Operations}
|
|
|
|
@prim-op-defns['(lib "htdp-beginner.ss" "lang") #'here '()]
|