doc edits

svn: r6255
This commit is contained in:
Matthew Flatt 2007-05-24 06:04:01 +00:00
parent 8ab6ad2c9c
commit 49740192d5
5 changed files with 139 additions and 140 deletions

View File

@ -45,10 +45,10 @@ Many predefined procedures operate on lists. Here are a few examples:
In addition to simple operations like @scheme[append], Scheme includes
procedures that iterate over the elements of a list. These iteration
procedures play the same role as, say, @tt{for} in Java. The body of
an iteration is packaged into a procedure to be applied to each
element, so the @scheme[lambda] form becomes particularly handy in
combination with iteration procedures.
procedures play much the same role as @tt{for} in Java and other
languages. The body of a Scheme iteration is packaged into a procedure
to be applied to each element, so the @scheme[lambda] form becomes
particularly handy in combination with iteration procedures.
The @scheme[for-each] procedure acts the most like a @tt{for} loop:
@ -58,12 +58,14 @@ The @scheme[for-each] procedure acts the most like a @tt{for} loop:
(list "pie" "stew" "carrots and pizza, and pineapple, too"))
]
Scheme includes many other list-iteration procedures, because there
are multiple ways to combine the results from each iteration. The
@scheme[for-each] procedure completely ignores the per-element result,
so it is used with a loop body that has some side-effect (such as
printing output). The @scheme[map] procedure, in contrast, uses the
per-element results to create a new list:
The @scheme[for-each] procedure completely ignores the per-element
result of the iteration body, so it is used with loop bodies that have
a side-effect (such as printing output). Keeping in mind that Scheme
programmers avoid side-effects, they also avoid @scheme[for-each].
Other list-iteration procedures use the per-element results, but in
different ways. The @scheme[map] procedure uses the per-element
results to create a new list:
@interaction[
(map sqrt (list 1 4 9 16))
@ -81,10 +83,18 @@ by @scheme[and]ing or @scheme[or]ing:
(ormap number? (list "a" "b" 6))
]
The @scheme[for-each], @scheme[map], @scheme[andmap], and
@scheme[ormap] procedures can all handle multiple lists, instead of
just a single list. The lists must all have the same length, and the
given procedure must accept one argument for each list:
The @scheme[filter] procedure keeps elements for which the body result
is true, and discards elements for which it is @scheme[#f]:
@interaction[
(filter string? (list "a" "b" 6))
(filter positive? (list 1 -2 6 7 0))
]
The @scheme[for-each], @scheme[map], @scheme[andmap], @scheme[ormap],
and @scheme[filter] procedures can all handle multiple lists, instead
of just a single list. The lists must all have the same length, and
the given procedure must accept one argument for each list:
@interaction[
(map (lambda (s n) (substring s 0 n))
@ -95,7 +105,7 @@ given procedure must accept one argument for each list:
The @scheme[foldl] procedure generalizes some iteration procedures. It
uses the per-element procedure to both process an element and combine
it with the ``current'' value, so the per-element procedure takes an
extra first argument. Also, a starting ``current ' value must be
extra first argument. Also, a starting ``current'' value must be
provided before the lists:
@interaction[
@ -106,9 +116,9 @@ provided before the lists:
]
Despite its generality, @scheme[foldl] is not as popular as the other
procedures. The main reason is that @scheme[for-each], @scheme[map],
@scheme[ormap], and @scheme[andmap] cover the most common kinds of
loops.
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[fold-for] and @scheme[list-for]}
@ -151,7 +161,7 @@ and implicitly accumulates each result into a list:
]
The @scheme[list-for] form is a @defterm{list compherension} form, as
in Haskell, Ruby, Python, and other languages. Its advantage over
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[list-for] can iterate over a range of numbers:
@ -191,15 +201,15 @@ simpler (just a procedure call).
We have ignored several other variants of the interation
form---including plain @scheme[for], which is use when the iteration
body is to be run only for its effect. We explain the full set in
@secref["iterations+comprehensions"].
body is to be run only for its effect. For more complete information,
see @secref["iterations+comprehensions"].
@;------------------------------------------------------------------------
@section{List Iteration from Scratch}
Although @scheme[map] and @scheme[list-for] are predefined, they are
not primitive in any interesting sense. You can write equivalent
iterations using just a handful of list primitives.
iterations using a handful of list primitives.
Since a Scheme list is a linked list, the two core operations on a
non-empty list are
@ -231,8 +241,7 @@ empty
To process a list, you need to be able to distinguish empty lists from
non-empty lists, because @scheme[first] and @scheme[rest] work only on
non-empty lists. The @scheme[empty?] procedure detects empty lists,
and @scheme[cons?] detects non-empty lists (which pair an element with
another list):
and @scheme[cons?] detects non-empty lists:
@interaction[
(empty? empty)
@ -261,14 +270,13 @@ With these pieces, you can write your own versions of the
(my-map string-upcase (list "ready" "set" "go"))
]
If the definitions are 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.
If the derivation of the above definitions is mysterious to you,
consider reading @|HtDP|. But if you are merely suspicious of the use
of recursive calls instead of a looping construct, then read on.
Both the @scheme[my-length] and @scheme[my-map] procedures 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
sub-expression:
imagining how @scheme[(my-length (list "a" "b" "c"))] must evaluate:
@schemeblock[
(my-length (list "a" "b" "c"))
@ -283,8 +291,13 @@ sub-expression:
For a list with @math{n} elements, evalution will stack up @math{n}
@scheme[(+ 1 ...)] additions, and then finally add them up when the
list is exhausted. You can avoid piling up additions by adding along
the way, accumulating the length in a variable @scheme[len].
list is exhausted.
You can avoid piling up additions by adding along the way. To
accumulate a length this way, we need a procedure that takes both a
list and the length of the list seem so far; the code below uses a
local procedure @scheme[iter] that accumulates the length in an
argument @scheme[len]:
@schemeblock[
(define (my-length lst)
@ -310,16 +323,18 @@ Now evaluation looks like this:
The revised @scheme[my-length] runs in constant space, just as the
evaluation steps above suggest. That is, when the result of a
procedure call is the result of some other procedure call, then the
original procedure doesn't have to wait around for the result, taking
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.
procedure call, like @scheme[(iter (list "b" "c") 1)], is exactly the
result of some other procedure call, like @scheme[(iter (list "c")
2)], then the first one doesn't have to wait around for the second
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.
In the case of @scheme[my-map], @math{O(n)} space compelxity is
reasonable, since it has to generate an @math{O(n)}
result. Nevertheless, you can reduce the constant factor by
reasonable, since it has to generate a result of size
@math{O(n)}. Nevertheless, you can reduce the constant factor by
accumulating the result list. The only catch is that the accumulated
list will be backwards, so you'll have to reverse it at the very end:
@ -350,11 +365,11 @@ use. The difference is merely syntactic convenience.
@section{Recursion versus Iteration}
The @scheme[my-length] and @scheme[my-map] examples demonstrate that
iteration is just a special case of recursion. In many languages, it
is important to try to fit as many computations as possible into
iteration form, otherwise performance will be bad and moderately large
inputs can lead to stack overflow. Similarly, in Scheme, it is often
important to make sure that tail recursion is used to avoid
iteration is just a special case of recursion. In many languages, it's
important to try to fit as many computations as possible into
iteration form. Otherwise, performance will be bad, and moderately
large inputs can lead to stack overflow. Similarly, in Scheme, it is
often important to make sure that tail recursion is used to avoid
@math{O(n)} space consumption when the computation is easily performed
in constant space.
@ -368,9 +383,9 @@ tail-recursive programs automatically run the same as a loop, lead
Scheme programmers to embrace recursive forms rather than avoid them.
Suppose, for example, that you want to remove consecutive duplicates
from a list. While that can be written as a loop that remembers the
previous element for each iteration, a Scheme programmer would more
likely just write the following:
from a list. While that procedure can be written as a loop that
remembers the previous element for each iteration, a Scheme programmer
would more likely just write the following:
@def+int[
(define (remove-dups l)
@ -405,22 +420,22 @@ kicks in:
]
Tail-call behavior becomes even more important when dealing with
non-list data and when using an object-oriented style. In the latter
case, an object must frequently dispatch to another object; if the
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 this extended discussion
until @secref["datatypes"], at which point we'll have more forms of
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 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{identifier} after the
@scheme[let] (instead of an immediate open parenthesis) triggers a
different parsing. In general,
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{identifier} after the @scheme[let] (instead of an immediate
open parenthesis) triggers a different parsing. In general,
@schemeblock[
#, @BNF-seq[@litchar{(} @litchar{let} @nonterm{proc-identifier} @litchar{(}
@ -442,9 +457,10 @@ 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. It looks
similar to the start of @scheme[fold-for], but the recursive calls in
the body are explicit, and they are not constrained to tail position.
procedure with the values of some initial expressions. A named
@scheme[let] looks similar to the start of @scheme[fold-for], 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:

View File

@ -4,7 +4,7 @@
@require[(lib "bnf.ss" "scribble")]
@require["guide-utils.ss"]
@title[#:tag "syntax-overview"]{Just Enough Scheme Syntax}
@title[#:tag "syntax-overview"]{Basic Scheme Syntax}
The syntax of a Scheme program is specified in an unusual way compared
to most programming languages. In particular, importing a module can
@ -92,9 +92,10 @@ binds @nonterm{identifier} to the result of @nonterm{expression}, while
@schemeblock[#, @fun-defn-stx]
binds the first @nonterm{identifier} to a procedure that takes
arguments as named by the remaining @nonterm{identifier}s. The
@nonterm{expression}s are the body of the procedure. When called, the
procedure returns the result of the last @nonterm{expression}.
arguments as named by the remaining @nonterm{identifier}s. In the
procedure case, the @nonterm{expression}s are the body of the
procedure. When the procedure is called, it returns the result of the
last @nonterm{expression}.
@defexamples[
(code:line (define five 5) (code:comment #, @t{defines @scheme[five] to be @scheme[5]}))
@ -105,7 +106,7 @@ five
]
Under the hood, a procedure definition is really the same as a
non-procedure definition. Consequently, a procedure name does not have to be
non-procedure definition, and a procedure name does not have to be
used in a procedure call. A procedure is just another kind of value,
though the printed form is necessarily less complete than the printed
form of a number or string.
@ -139,10 +140,12 @@ evaluated only for some side-effect, such as printing.
(greet "universe")
]
Although you should generally avoid side-effects, it's important to
understand that multiple expressions are allowed in a definition
body. It explains why the following @scheme[nogreet] procedure simply
returns its argument:
You should generally avoid side-effects in Scheme; printing is a
reasonable effect to use in some programs, but it's no substitute for
simply returning a value. In any case, you should understand that
multiple expressions are allowed in a definition body, because it
explains why the following @scheme[nogreet] procedure simply returns
its argument:
@def+int[
(define (nogreet name)
@ -150,12 +153,12 @@ returns its argument:
(nogreet "world")
]
There are no parentheses around @scheme[string-append "hello " name],
so they are three separate expressions instead of one procedure-call
expression. The expressions @scheme[string-append] and
@scheme["hello "] are evaluated, but the results are never
used. Instead, the result of the procedure is just the result of
the expression @scheme[name].
Withing @scheme[nogreet], there are no parentheses around
@scheme[string-append "hello " name], so they are three separate
expressions instead of one procedure-call expression. The expressions
@scheme[string-append] and @scheme["hello "] are evaluated, but the
results are never used. Instead, the result of the procedure is just
the result of the expression @scheme[name].
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@section{Identifiers}
@ -251,7 +254,7 @@ string-append
We have already seen many procedure calls---or @defterm{procedure
applications} in Scheme termonology. The syntax of a procedure
call is
application is
@schemeblock[
#, app-expr-stx
@ -289,8 +292,7 @@ click on an identifier to get full details about its use.
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@section{Conditionals with @scheme[if], @scheme[and], @scheme[or], and @scheme[cond]}
After identifiers and constants, the next simplest kind of expression
is an @scheme[if] conditional:
The next simplest kind of expression is an @scheme[if] conditional:
@schemeblock[
#, if-expr-stx
@ -301,10 +303,7 @@ non-@scheme[#f] value, then the second @nonterm{expression} is
evaluted for the result of the whole @scheme[if] expression, otherwise
the third @nonterm{expression} is evaluated for the result.
The @scheme[if] form is often used with procedures whose names end in
@schemeid[?]:
@interaction[
@examples[
(if (> 2 3)
"bigger"
"smaller")
@ -319,7 +318,7 @@ The @scheme[if] form is often used with procedures whose names end in
(reply "\u03BBx:(\u03BC\u03B1.\u03B1\u2192\u03B1).xx")
]
More complex conditionals can be formed by nesting @scheme[if]
Complex conditionals can be formed by nesting @scheme[if]
expressions. For example, you could make the @scheme[reply] procedure
work when given non-strings:
@ -345,7 +344,7 @@ better written as
]
but these kinds of nested @scheme[if]s are difficult to read. Scheme
provides a more readable shortcut through the @scheme[and] and
provides more readable shortcuts through the @scheme[and] and
@scheme[or] forms, which work with any number of expressions:
@schemeblock[
@ -396,8 +395,8 @@ expression. If it produces true, then the clause's second
expression, and the rest of the clauses are ignored. If the test
@nonterm{expression} produces @scheme[#f], then the clause's second
@nonterm{expression} is ignored, and evaluation continues with the
next clause. The last clause can use @scheme[else] as a sononym for
@scheme[#t] in place of a test expression.
next clause. The last clause can use @scheme[else] as a synonym for
a @scheme[#t] test expression.
Using @scheme[cond], the @scheme[reply-more] procedure can be more
clearly written as follows:
@ -458,13 +457,6 @@ When you accidentally omit a procedure name or when you use
parentheses around an expression, you'll most often get an ``expected
a procedure'' error like this one.
Technically, @scheme[(if #t 1 2)] can be parsed as a procedure
application as well as a conditional in our pretend grammar of Scheme,
since @schemeid[if] is an identifier. For now, we say that the
application form has a weaker precedence than the other forms, and
we'll leave a full explanation to our discussion of
@secref["scheme-forms"].
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@section{Anonymous Procedures with @scheme[lambda]}
@ -659,12 +651,8 @@ too simple even for this chapter. Here's the grammar that we have now:
@BNF-etc)]
For an expanded grammar, it's still a pretty small language! This
language is plenty, however, to write lots of interesting programs.
language is enough, however, to write lots of interesting programs.
Depending on your programming background, you may be struck by the
apparent absence of an iteration form. But the above is enough to
write any kind of loop, as we see in the next chapter. Moreover, an
extra built-in datatype (to go along numbers, strings, etc.) gives us
more interesting data to process with loops. The new datatype is also
the key to understanding the true syntax of Scheme, which we get back
to in @secref["scheme-read"] and @secref["scheme-forms"].
apparent absence of an iteration form. We'll add one in the next
chapter, but also explain why it isn't really necessary.

View File

@ -9,7 +9,7 @@
The @scheme[cons] procedure actually accepts any two values, not just
a list for the second argument. When the second argument is not
@scheme[empty] or itself produced by @scheme[cons], the result prints
@scheme[empty] and not itself produced by @scheme[cons], the result prints
in a special way. The two values joined with @scheme[cons] are printed
between parentheses, but with a dot (i.e., a period surrounded by
whitespace) in between:
@ -66,7 +66,7 @@ becomes @schemeresult[(0 1 . 2)], and
@schemeresultfont{(1 . (2 . (3 . ())))} becomes @schemeresult[(1 2 3)].
@;------------------------------------------------------------------------
@section{Quoting Lists and Symbols with @scheme[quote]}
@section{Quoting Pairs and Symbols with @scheme[quote]}
After you see
@ -145,9 +145,9 @@ identifiers, it creates a list of symbols:
@;------------------------------------------------------------------------
@section{Abbreviating @scheme[quote] with @schemevalfont{'}}
If @scheme[(#, @scheme[quote] (1 2 3))] is still too much typing, you
can abbreviate by just putting @litchar{'} in front of @scheme[(1 2
3)]:
If @scheme[(#, @scheme[quote] (1 2 3))] still seems like too much
typing, you can abbreviate by just putting @litchar{'} in front of
@scheme[(1 2 3)]:
@interaction[
'(1 2 3)
@ -173,8 +173,8 @@ quote:
]
Beware, however, that the REPL's printer recognizes the symbol
@schemeidfont{quote} hwne printing output, and it uses @schemeidfont{'}
in the output:
@schemeidfont{quote} when printing output, and then it uses
@schemeidfont{'} in the output:
@interaction[
'road
@ -182,7 +182,7 @@ in the output:
(eval:alts '(#, @schemevalfont{quote} #, @schemevalfont{road}) ''road)
]
There is a method to this madness. It has to do with the true nature
There is a method to this madness; it has to do with the true nature
of Scheme syntax (which we discuss in the next section) and the
traditional Lisp approach to meta-programming (which we discuss in the
@seclink["quote-eval"]{section afterward}).
@ -207,11 +207,11 @@ streams. Instead, the syntax is determined by two layers:
}
The rules for printing and the read layer go together. For example, a
list is printed with parentheses, and reading a pair of parentheses
produces a list. Similarly, a non-list pair is printed with the dot
notation, and a dot on input effectively runs the dot-notation rules
in reverse to obtain a pair.
The rules for printing and reading go together. For example, a list is
printed with parentheses, and reading a pair of parentheses produces a
list. Similarly, a non-list pair is printed with the dot notation, and
a dot on input effectively runs the dot-notation rules in reverse to
obtain a pair.
One consequence of the read layer for expressions is that you can use
the dot notation in expressions that are not quoted forms:
@ -221,8 +221,9 @@ the dot notation in expressions that are not quoted forms:
]
This works because @scheme[(+ 1 . #, @scheme[(2)])] is just another
way of writing @scheme[(+ 1 2)]. Of course, it is practically never a
good idea to write application expressions using this dot notation.
way of writing @scheme[(+ 1 2)]. It is practically never a good idea
to write application expressions using this dot notation; it's just a
consequence of the way Scheme's syntax is defined.
The rule for converting @litchar{'} to a use of @scheme[quote] is also
defined at the read level. If you (accidentally) use
@ -244,8 +245,8 @@ when you understand the reader's conversion:
The second example fails because @scheme['#, @schemeidfont{shakey}] is
really @scheme[(#, @schemeidfont{quote} #, @schemeidfont{shakey})],
which means the application of the @scheme[shakey] procedure's
argument (which turns out to be a string, not a procedure).
which means the application of @scheme[shakey]'s argument (which turns
out to be a string, not a procedure).
A few other character combinations trigger @litchar{'}-like
conversions, including @litchar{`}, @litchar{,}, @litchar["@,"], and
@ -257,9 +258,9 @@ Normally, @litchar{.} is allowed by the reader only with a
parenthesized sequence, and only before the last element of the
sequence. However, a pair of @litchar{.}s can also appear around a
single element in a parenthesized sequence, as long as the element is
not first or last. Such a pair triggers a reader conversion that
moves the element between @litchar{.}s to the front of the list, which
enables a kind of general infix notation:
not first or last. Such a pair triggers a reader conversion that moves
the element between @litchar{.}s to the front of the list. The
conversion enables a kind of general infix notation:
@interaction[
(1 . < . 2)

View File

@ -39,15 +39,10 @@ command-line @exec{mzscheme} interpreter and your favorite text
editor. The rest of this guide presents the language mostly
independent of the tool that you use.
If you read @Quick and you're still using DrScheme, you already know
about interactions and definitions. You can safely skip to
@secref["indentation"], with the caveat that we call the interactions
window the @defterm{REPL} in this guide.
Otherwise, if you're using DrScheme, you'll need to set it in the
right mode, because DrScheme is less biased to a particular Scheme
variant than @exec{mzscheme}. Assuming that you've never used DrScheme
before, start it up, type the line
If you're using DrScheme, you'll need to set it in the right mode,
because DrScheme is less biased to a particular Scheme variant than
@exec{mzscheme}. Assuming that you've never used DrScheme before,
start it up, type the line
@schememod[big]
@ -167,7 +162,7 @@ bad performance, and awkward scripting to combine and run
programs. The problems are not in @exec{mzscheme}'s implementation;
they're fundamental limitations of the traditional top-level
environment, which Scheme and Lisp implementations have historically
combated through ad hoc command-line flags, compiler directives, and
fought with ad hoc command-line flags, compiler directives, and
build tools. The module system is to designed to avoid the problems,
so start with @schemefont{#module}, and you'll be happier with Scheme
in the long run.

View File

@ -227,7 +227,7 @@ anonymous procedure:
@mr-interaction[(series (lambda (size) (checkerboard (square size))))]
The parenthesized name(s) after a @scheme[lambda] are the argument to
The parenthesized names after a @scheme[lambda] are the arguments to
the procedure, and the expression after the argument names is the
procedure body. Using the word ``lambda'' instead of ``function'' or
``procedure'' is part of Scheme's history and culture.
@ -243,10 +243,7 @@ A procedure-form @scheme[define] is really a shorthand for a simple
]
Most Schemers prefer to use the shorthand procedure form with
@scheme[define] instead of expanding to @scheme[lambda], but
@scheme[lambda] shows up often with @scheme[letrec] to define mutually
recursive procedures; see @link["elsewhere"]{elsewhere} for
examples.
@scheme[define] instead of expanding to @scheme[lambda].
@; ----------------------------------------------------------------------
@section{Lexical Scope}
@ -258,8 +255,8 @@ binding. This rule applies to identifiers in a @scheme[lambda] body as
well as anywhere else.
For example, in the following @scheme[color-series] procedure the uses
of @scheme[mk] in each @scheme[lambda] form refer to the argument of
@scheme[color-series], since that's the binding that textually in
of @scheme[mk] in each @scheme[lambda] form to refer to the argument of
@scheme[color-series], since that's the binding that is textually in
scope:
@mr-def+int[
@ -300,7 +297,7 @@ The @scheme[list] procedure takes any number of arguments and returns
a list containing the given values:
@mr-interaction[(list "red" "green" "blue")
(list (circle 10) (square 10))]
(list (circle 10) (square 10))]
As you can see, a list prints as a pair of parentheses wrapped around
the printed form of the list elements. There's room for confusion
@ -308,12 +305,14 @@ here, because parentheses are used for both expressions, such as
@scheme[(circle 10)], and printed results, such as
@schemeresult[("red" "green" "blue")]. This connection between
expressions and printed results is no coincidence, but we save that
bit of culture for @link["elsewhere"]{discussion elsewhere}.
bit of culture for @link["elsewhere"]{discussion elsewhere}. In the
documentation and in DrScheme, result parentheses are printed in blue,
unlike expression parentheses.
If you have a list, then you'll eventually want to do something with
each of the elements. The @scheme[map] procedure takes a list and a
procedure to apply to each element of the list; it returns a new list
to report the procedure's results:
to combine the procedure's results:
@mr-def+int[
(define (rainbow p)
@ -444,7 +443,7 @@ that @schememodname[little] provides @scheme[require] and the
procedure-calling syntax. Libraries are not restricted to exporting
values, such as procedures; they can also define new syntax. In this
sense, Scheme isn't exactly language at all; it's more of an idea for
how to structure a language to that you can extend it or create
how to structure a language so that you can extend it or create
entirely new languages.
One way to introduce a new syntactic form is through