updates
parent
5488a6baef
commit
09e7709eee
@ -1,14 +0,0 @@
|
|||||||
#lang racket/base
|
|
||||||
(require racket/contract racket/match racket/set)
|
|
||||||
(require sugar txexpr)
|
|
||||||
(require "world.rkt" "file-tools.rkt" "debug.rkt")
|
|
||||||
|
|
||||||
(provide (all-from-out "file-tools.rkt"))
|
|
||||||
|
|
||||||
;; test for well-formed meta
|
|
||||||
(define+provide/contract (meta-xexpr? x)
|
|
||||||
(any/c . -> . boolean?)
|
|
||||||
(match x
|
|
||||||
[`(meta ,(? string? key) ,(? string? value)) #t]
|
|
||||||
[else #f]))
|
|
||||||
|
|
@ -0,0 +1,177 @@
|
|||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require scribble/eval pollen/render pollen/world (for-label racket (except-in pollen #%module-begin) pollen/world pollen/file sugar/coerce/value))
|
||||||
|
|
||||||
|
@(define my-eval (make-base-eval))
|
||||||
|
@(my-eval `(require pollen pollen/file))
|
||||||
|
|
||||||
|
@section{Files}
|
||||||
|
|
||||||
|
@defmodule[pollen/file]
|
||||||
|
|
||||||
|
A utility module that provides functions for working with Pollen source and output files. The tests rely on file extensions specified in @racket[pollen/world].
|
||||||
|
|
||||||
|
Pollen handles six kinds of source files:
|
||||||
|
|
||||||
|
@bold{Preprocessor}, with file extension @code[(format ".~a" world:preproc-source-ext)].
|
||||||
|
|
||||||
|
@bold{Markup}, with file extension @code[(format ".~a" world:markup-source-ext)].
|
||||||
|
|
||||||
|
@bold{Template}, with file extension @code[(format ".~a" world:template-source-ext)].
|
||||||
|
|
||||||
|
@bold{Null}, with file extension @code[(format ".~a" world:null-source-ext)].
|
||||||
|
|
||||||
|
@bold{Scribble}, with file extension @code[(format ".~a" world:scribble-source-ext)].
|
||||||
|
|
||||||
|
For each kind of Pollen source file, the corresponding output file is generated by removing the extension from the name of the source file. So the preprocessor source file @code["default.css.pp"] would become @code["default.css"]. Scribble files work differently — the corresponding output file is the source file but with an @racket[html] extension rather than @racket[scrbl]. So @code["pollen.scrbl"] would become @code["pollen.html"].
|
||||||
|
|
||||||
|
@deftogether[
|
||||||
|
(@defproc[
|
||||||
|
(preproc-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(markup-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(template-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(null-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(scribble-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(ptree-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
)]
|
||||||
|
Test whether @racket[_v] is a path representing a source file of the specified type, based on file extension.
|
||||||
|
|
||||||
|
@examples[#:eval my-eval
|
||||||
|
(preproc-source? "main.css.pp")
|
||||||
|
(markup-source? "default.html.pm")
|
||||||
|
(template-source? "main.html.pt")
|
||||||
|
(null-source? "index.html.p")
|
||||||
|
(scribble-source? "file.scrbl")
|
||||||
|
(ptree-source? "index.ptree")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@deftogether[
|
||||||
|
(@defproc[
|
||||||
|
(has-preproc-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(has-markup-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(has-template-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(has-null-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(has-scribble-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
)]
|
||||||
|
Test whether @racket[_v] is the output path for an existing source file of the specified type.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@deftogether[
|
||||||
|
(@defproc[
|
||||||
|
(has/is-preproc-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(has/is-markup-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(has/is-template-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(has/is-null-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(has/is-scribble-source?
|
||||||
|
[v any/c])
|
||||||
|
boolean?]
|
||||||
|
)]
|
||||||
|
Test whether @racket[_v] is a path representing a source file of the specified type, or is the output path for an existing source file of the specified type. In other words, @racket[has/is-preproc-source?] is equivalent to @racket[(or (preproc-source? v) (has-preproc-source? v))].
|
||||||
|
|
||||||
|
|
||||||
|
@deftogether[
|
||||||
|
(@defproc[
|
||||||
|
(->preproc-source-path
|
||||||
|
[p pathish?])
|
||||||
|
path?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(->markup-source-path
|
||||||
|
[p pathish?])
|
||||||
|
path?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(->template-source-path
|
||||||
|
[p pathish?])
|
||||||
|
path?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(->null-source-path
|
||||||
|
[p pathish?])
|
||||||
|
path?]
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(->scribble-source-path
|
||||||
|
[p pathish?])
|
||||||
|
path?]
|
||||||
|
)]
|
||||||
|
Convert an output path @racket[_p] into the source path of the specified type that would produce this output path. Does not generate this source file nor verify that it exists.
|
||||||
|
|
||||||
|
@examples[#:eval my-eval
|
||||||
|
(define name "default.html")
|
||||||
|
(->preproc-source-path name)
|
||||||
|
(->markup-source-path name)
|
||||||
|
(->template-source-path name)
|
||||||
|
(->scribble-source-path name)
|
||||||
|
(->null-source-path name)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@defproc[
|
||||||
|
(->output-path
|
||||||
|
[p pathish?])
|
||||||
|
path?]
|
||||||
|
Convert a source path @racket[_p] into its corresponding output path.
|
@ -1,609 +0,0 @@
|
|||||||
#lang scribble/doc
|
|
||||||
|
|
||||||
@title{Quick: An Introduction to Racket with Pictures}
|
|
||||||
|
|
||||||
@author["Matthew Flatt"]
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
@(require scribble/manual
|
|
||||||
"mreval.rkt"
|
|
||||||
"keep.rkt"
|
|
||||||
scribble/urls
|
|
||||||
scribble/struct
|
|
||||||
racket/class
|
|
||||||
|
|
||||||
(for-label racket/base
|
|
||||||
racket/gui/base
|
|
||||||
racket/class
|
|
||||||
slideshow pict
|
|
||||||
slideshow/code
|
|
||||||
pict/flash)
|
|
||||||
|
|
||||||
(for-syntax racket/base))
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
This tutorial provides a brief introduction to the Racket
|
|
||||||
programming language by using one of its picture-drawing
|
|
||||||
libraries. Even if you don't intend to use Racket for your artistic
|
|
||||||
endeavours, the picture library supports interesting and enlightening
|
|
||||||
examples. After all, a picture is worth five hundred ``hello world''s.
|
|
||||||
|
|
||||||
Along the same lines, we assume that you will run the examples using
|
|
||||||
@link[url:download-drracket]{DrRacket}. Using DrRacket is the fastest
|
|
||||||
way to get a sense of what the language and system feels like, even if
|
|
||||||
you eventually use Racket with Emacs, vi, or some other editor.
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Ready...}
|
|
||||||
|
|
||||||
@link[url:download-drracket]{Download Racket}, install, and then
|
|
||||||
start DrRacket.
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Set...}
|
|
||||||
|
|
||||||
@margin-note{See @seclink[#:indirect? #t
|
|
||||||
#:doc '(lib "scribblings/drracket/drracket.scrbl")
|
|
||||||
"interface-essentials"]{the DrRacket documentation}
|
|
||||||
for a brief overview of
|
|
||||||
the DrRacket IDE.}
|
|
||||||
To draw pictures, we must first load some picture functions, which are
|
|
||||||
part of a library for creating slide presentations. Copy the
|
|
||||||
following into the @defterm{definitions area}, which is the top text
|
|
||||||
area that you see in DrRacket:
|
|
||||||
|
|
||||||
@racketmod[slideshow]
|
|
||||||
|
|
||||||
Then click the @onscreen{Run} button. You'll see the text caret move
|
|
||||||
to the bottom text area, which is the @defterm{interactions area}.
|
|
||||||
|
|
||||||
If you've used DrRacket before, you might need to reset DrRacket to
|
|
||||||
use the language declared in the source via the @menuitem["Language"
|
|
||||||
"Choose Language..."] menu item before clicking @onscreen{Run}.
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Go!}
|
|
||||||
|
|
||||||
When you type an expression after the @onscreen{>} in the interactions
|
|
||||||
window and hit Enter, DrRacket evaluates the expression and prints its
|
|
||||||
result. An expression can be just a value, such as the number
|
|
||||||
@racket[5] or the string @racket["art gallery"]:
|
|
||||||
|
|
||||||
@ss-interaction[5 "art gallery"]
|
|
||||||
|
|
||||||
An expression can also be a function call. To call a function, put
|
|
||||||
an open parenthesis before the function name, then expressions for the
|
|
||||||
function arguments, and then a close parenthesis, like this:
|
|
||||||
|
|
||||||
@ss-interaction[(circle 10)]
|
|
||||||
|
|
||||||
A result from the @racket[circle] function is a picture value, which
|
|
||||||
prints as an expression result in much the same way that numbers or
|
|
||||||
strings print. The argument to @racket[circle] determines the
|
|
||||||
circle's size in pixels. As you might guess, there's a
|
|
||||||
@racket[rectangle] function that takes two arguments instead of one:
|
|
||||||
|
|
||||||
@ss-interaction[(rectangle 10 20)]
|
|
||||||
|
|
||||||
Try giving @racket[circle] the wrong number of arguments, just to see
|
|
||||||
what happens:
|
|
||||||
|
|
||||||
@ss-interaction[(circle 10 20)]
|
|
||||||
|
|
||||||
Note that DrRacket highlights in pink the expression that triggered
|
|
||||||
the error (but pink highlighting is not shown in this documentation).
|
|
||||||
|
|
||||||
In addition to basic picture constructors like @racket[circle] and
|
|
||||||
@racket[rectangle], there's a @racket[hc-append] function that
|
|
||||||
combines pictures. When you start composing function calls in Racket,
|
|
||||||
it looks like this:
|
|
||||||
|
|
||||||
@ss-interaction[(hc-append (circle 10) (rectangle 10 20))]
|
|
||||||
|
|
||||||
The hyphen in the name @racket[hc-append] is just a part of the
|
|
||||||
identifier; it's not @racketidfont{hc} minus
|
|
||||||
@racketidfont{append}. The function name starts with @racket[h]
|
|
||||||
because it combines pictures horizontally, and the next letter is
|
|
||||||
@racket[c] because the pictures are centered vertically.
|
|
||||||
|
|
||||||
If you wonder what other functions exist---perhaps a way to stack
|
|
||||||
pictures vertically and left-aligned?---move the text caret to the
|
|
||||||
name @racket[hc-append] and press the F1 key in DrRacket. A browser
|
|
||||||
window will open, and it will give you a link to the documentation for
|
|
||||||
@racket[hc-append]. Click the link, and you'll see lots of other
|
|
||||||
functions.
|
|
||||||
|
|
||||||
If you're reading this in HTML form, you can also just click on
|
|
||||||
@racket[hc-append] or any other imported identifier that is used in
|
|
||||||
this tutorial.
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Definitions}
|
|
||||||
|
|
||||||
To use a particular circle and rectangle picture many times, it's
|
|
||||||
simpler to give them names. Move back to the definitions area (the top
|
|
||||||
area) and add two definitions, so that the complete content of the
|
|
||||||
definitions area looks like this:
|
|
||||||
|
|
||||||
@ss-racketmod+eval[
|
|
||||||
slideshow
|
|
||||||
(define c (circle 10))
|
|
||||||
(define r (rectangle 10 20))
|
|
||||||
]
|
|
||||||
|
|
||||||
Then click @onscreen{Run} again. Now, you can just type @racket[c] or
|
|
||||||
@racket[r]:
|
|
||||||
|
|
||||||
@ss-interaction[r (hc-append c r) (hc-append 20 c r c)]
|
|
||||||
|
|
||||||
As you can see, the @racket[hc-append] function accepts an optional
|
|
||||||
number argument before the picture arguments, and it accepts any
|
|
||||||
number of picture arguments. When a number is provided, it specifies
|
|
||||||
the amount of space to add between pictures.
|
|
||||||
|
|
||||||
We could have evaluated the @racket[define] forms for @racket[c] and
|
|
||||||
@racket[r] in the interactions area instead of the definitions
|
|
||||||
area. In practice, though, the definitions area is where your program
|
|
||||||
lives---it's the file that you save---while the interaction area is
|
|
||||||
for transient explorations and debugging tasks.
|
|
||||||
|
|
||||||
Let's add a function definition to the program. A function definition
|
|
||||||
uses @racket[define], just like our shape definitions, but with an
|
|
||||||
open parenthesis before the function name, and names for the function
|
|
||||||
arguments before the matching close parenthesis:
|
|
||||||
|
|
||||||
@ss-racketblock+eval[
|
|
||||||
(define (square n)
|
|
||||||
(code:comment @#,t{A semi-colon starts a line comment.})
|
|
||||||
(code:comment @#,t{The expression below is the function body.})
|
|
||||||
(filled-rectangle n n))
|
|
||||||
]
|
|
||||||
|
|
||||||
The syntax of the definition mirrors the syntax of a function
|
|
||||||
call:
|
|
||||||
|
|
||||||
@ss-interaction[(square 10)]
|
|
||||||
|
|
||||||
In the same way that definitions can be evaluated in the interactions
|
|
||||||
area, expressions can be included in the definitions area. When a
|
|
||||||
program is run, expression results from the definition area are shown
|
|
||||||
in the interaction area. From now on, we'll write our example
|
|
||||||
definitions and expressions together, and you can put them in
|
|
||||||
whichever area you prefer. The examples will build on each other,
|
|
||||||
however, so it's best to put at least the definitions in the
|
|
||||||
definition area.
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Local Binding}
|
|
||||||
|
|
||||||
The @racket[define] form can be used in some places to create local
|
|
||||||
bindings. For example, it can be used inside a function body:
|
|
||||||
|
|
||||||
@ss-def+int[
|
|
||||||
(define (four p)
|
|
||||||
(define two-p (hc-append p p))
|
|
||||||
(vc-append two-p two-p))
|
|
||||||
(four (circle 10))
|
|
||||||
]
|
|
||||||
|
|
||||||
More typically, Racketeers use the @racket[let] or @racket[let*] form
|
|
||||||
for local binding. An advantage of @racket[let] is that it can be used
|
|
||||||
in any expression position. Also, it binds many identifiers at once,
|
|
||||||
instead of requiring a separate @racket[define] for each identifier:
|
|
||||||
|
|
||||||
@ss-def+int[
|
|
||||||
(define (checker p1 p2)
|
|
||||||
(let ([p12 (hc-append p1 p2)]
|
|
||||||
[p21 (hc-append p2 p1)])
|
|
||||||
(vc-append p12 p21)))
|
|
||||||
(checker (colorize (square 10) "red")
|
|
||||||
(colorize (square 10) "black"))
|
|
||||||
]
|
|
||||||
|
|
||||||
A @racket[let] form binds many identifiers at the same time, so the
|
|
||||||
bindings cannot refer to each other. The @racket[let*] form, in
|
|
||||||
contrast, allows later bindings to use earlier bindings:
|
|
||||||
|
|
||||||
@ss-def+int[
|
|
||||||
(define (checkerboard p)
|
|
||||||
(let* ([rp (colorize p "red")]
|
|
||||||
[bp (colorize p "black")]
|
|
||||||
[c (checker rp bp)]
|
|
||||||
[c4 (four c)])
|
|
||||||
(four c4)))
|
|
||||||
(checkerboard (square 10))
|
|
||||||
]
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Functions are Values}
|
|
||||||
|
|
||||||
Instead of calling @racket[circle] as a function, try evaluating just
|
|
||||||
@racket[circle] as an expression:
|
|
||||||
|
|
||||||
@ss-interaction[circle]
|
|
||||||
|
|
||||||
That is, the identifier @racket[circle] is bound to a function
|
|
||||||
(a.k.a. ``procedure''), just like @racket[c] is bound to a
|
|
||||||
circle. Unlike a circle picture, there's not a simple way of
|
|
||||||
completely printing the function, so DrRacket just prints
|
|
||||||
@procedure{circle}.
|
|
||||||
|
|
||||||
This example shows that functions are values, just like numbers and
|
|
||||||
pictures (even if they don't print as nicely). Since functions are
|
|
||||||
values, you can define functions that expect other functions as
|
|
||||||
arguments:
|
|
||||||
|
|
||||||
@ss-def+int[
|
|
||||||
(define (series mk)
|
|
||||||
(hc-append 4 (mk 5) (mk 10) (mk 20)))
|
|
||||||
(series circle)
|
|
||||||
(series square)
|
|
||||||
]
|
|
||||||
|
|
||||||
When calling a function that accepts a function argument, the
|
|
||||||
argument function often isn't needed anywhere else. Having to write
|
|
||||||
down the function via @racket[define] would be a hassle, because you
|
|
||||||
have to make up a name and find a place to put the function
|
|
||||||
definition. The alternative is to use @racket[lambda], which creates an
|
|
||||||
anonymous function:
|
|
||||||
|
|
||||||
@ss-interaction[(series (lambda (size) (checkerboard (square size))))]
|
|
||||||
|
|
||||||
The parenthesized names after a @racket[lambda] are the arguments to
|
|
||||||
the function, and the expression after the argument names is the
|
|
||||||
function body. Using the word ``lambda'' instead of ``function'' or
|
|
||||||
``procedure'' is part of Racket's history and culture.
|
|
||||||
|
|
||||||
A @racket[define] form for a function is really a shorthand for a
|
|
||||||
simple @racket[define] using @racket[lambda] as the value. For
|
|
||||||
example, the @racket[series] definition could be written as
|
|
||||||
|
|
||||||
@racketblock[
|
|
||||||
(define series
|
|
||||||
(lambda (mk)
|
|
||||||
(hc-append 4 (mk 5) (mk 10) (mk 20))))
|
|
||||||
]
|
|
||||||
|
|
||||||
Most Racketeers prefer to use the shorthand function form with
|
|
||||||
@racket[define] instead of expanding to @racket[lambda].
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Lexical Scope}
|
|
||||||
|
|
||||||
Racket is a lexically scoped language, which means that whenever an
|
|
||||||
identifier is used as an expression, something in the textual
|
|
||||||
environment of the expression determines the identifier's
|
|
||||||
binding. This rule applies to identifiers in a @racket[lambda] body as
|
|
||||||
well as anywhere else.
|
|
||||||
|
|
||||||
In the following @racket[rgb-series] function, the uses
|
|
||||||
of @racket[mk] in each @racket[lambda] form refer to the argument of
|
|
||||||
@racket[rgb-series], since that's the binding that is textually in
|
|
||||||
scope:
|
|
||||||
|
|
||||||
@ss-def+int[
|
|
||||||
(define (rgb-series mk)
|
|
||||||
(vc-append
|
|
||||||
(series (lambda (sz) (colorize (mk sz) "red")))
|
|
||||||
(series (lambda (sz) (colorize (mk sz) "green")))
|
|
||||||
(series (lambda (sz) (colorize (mk sz) "blue")))))
|
|
||||||
(rgb-series circle)
|
|
||||||
(rgb-series square)
|
|
||||||
]
|
|
||||||
|
|
||||||
Here's another example, where @racket[rgb-maker] takes a function and
|
|
||||||
returns a new one that remembers and uses the original function.
|
|
||||||
|
|
||||||
@ss-def+int[
|
|
||||||
(define (rgb-maker mk)
|
|
||||||
(lambda (sz)
|
|
||||||
(vc-append (colorize (mk sz) "red")
|
|
||||||
(colorize (mk sz) "green")
|
|
||||||
(colorize (mk sz) "blue"))))
|
|
||||||
(series (rgb-maker circle))
|
|
||||||
(series (rgb-maker square))
|
|
||||||
]
|
|
||||||
|
|
||||||
Note how composing functions via @racket[rgb-maker] creates a
|
|
||||||
different alignment of objects within the picture compared to using
|
|
||||||
@racket[rgb-series].
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Lists}
|
|
||||||
|
|
||||||
Racket inherits much of its style from the language Lisp, whose name
|
|
||||||
originally stood for ``LISt Processor,'' and lists remain an important
|
|
||||||
part of Racket.
|
|
||||||
|
|
||||||
The @racket[list] function takes any number of arguments and returns
|
|
||||||
a list containing the given values:
|
|
||||||
|
|
||||||
@ss-interaction[(list "red" "green" "blue")
|
|
||||||
(list (circle 10) (square 10))]
|
|
||||||
|
|
||||||
As you can see, a list prints as a single quote and then pair of parentheses wrapped around
|
|
||||||
the printed form of the list elements. There's room for confusion
|
|
||||||
here, because parentheses are used for both expressions, such as
|
|
||||||
@racket[(circle 10)], and printed results, such as
|
|
||||||
@racketresult['("red" "green" "blue")]. The quote is the key difference,
|
|
||||||
as @seclink[#:doc '(lib "scribblings/guide/guide.scrbl") "quoting-lists"]{discussed
|
|
||||||
elsewhere}. To help emphasize the difference, in the documentation and in DrRacket,
|
|
||||||
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 @racket[map] function takes a list and a
|
|
||||||
function to apply to each element of the list; it returns a new list
|
|
||||||
to combine the function's results:
|
|
||||||
|
|
||||||
@ss-def+int[
|
|
||||||
(define (rainbow p)
|
|
||||||
(map (lambda (color)
|
|
||||||
(colorize p color))
|
|
||||||
(list "red" "orange" "yellow" "green" "blue" "purple")))
|
|
||||||
(rainbow (square 5))
|
|
||||||
]
|
|
||||||
|
|
||||||
Another function that works with lists is @racket[apply]. Like
|
|
||||||
@racket[map], it takes a function and a list, but a function given
|
|
||||||
to @racket[apply] should take all of the arguments at once, instead of
|
|
||||||
each one individually. The @racket[apply] function is especially
|
|
||||||
useful with functions that take any number of arguments, such as
|
|
||||||
@racket[vc-append]:
|
|
||||||
|
|
||||||
@ss-interaction[
|
|
||||||
(apply vc-append (rainbow (square 5)))
|
|
||||||
]
|
|
||||||
|
|
||||||
Note that @racket[(vc-append (rainbow (square 5)))] would not work,
|
|
||||||
because @racket[vc-append] does not want a list as an argument; it
|
|
||||||
wants a picture as an argument, and it is willing to accept any number
|
|
||||||
of them. The @racket[apply] function bridges the gap between a
|
|
||||||
function that wants many arguments and a list of those arguments as a
|
|
||||||
single value.
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Modules}
|
|
||||||
|
|
||||||
Since your program in the definitions window starts with
|
|
||||||
|
|
||||||
@racketmod[slideshow]
|
|
||||||
|
|
||||||
all of the code that you put in the definitions window is inside a
|
|
||||||
module. Furthermore, the module initially imports everything from the
|
|
||||||
module designated by @racketmodname[slideshow], which exports
|
|
||||||
picture-making functions as well as more commonly used functions
|
|
||||||
such as @racket[list] and @racket[map].
|
|
||||||
|
|
||||||
To import additional libraries, use the @racket[require] form. For
|
|
||||||
example, the library @racketmodname[pict/flash] provides a
|
|
||||||
@racket[filled-flash] function:
|
|
||||||
|
|
||||||
@ss-def+int[
|
|
||||||
(require pict/flash)
|
|
||||||
(filled-flash 40 30)
|
|
||||||
]
|
|
||||||
|
|
||||||
Modules are named and distributed in various ways:
|
|
||||||
|
|
||||||
@itemize[
|
|
||||||
|
|
||||||
@item{Some modules are packaged in the Racket distribution or
|
|
||||||
otherwise installed into a hierarchy of
|
|
||||||
@defterm{collections}. For example, the module name
|
|
||||||
@racketmodname[pict/flash] means ``the module implemented
|
|
||||||
in the file @filepath{flash.rkt} that is located in the
|
|
||||||
@filepath{pict} collection.'' When a module name includes
|
|
||||||
no slash, then it refers to a @filepath{main.rkt} file.}
|
|
||||||
|
|
||||||
@item{Some modules are distributed through the
|
|
||||||
@link[url:planet]{@PLaneT} server, and they can be
|
|
||||||
downloaded automatically on demand. For example, the first time
|
|
||||||
that you evaluate the following fragment:
|
|
||||||
|
|
||||||
@mr-def+int[
|
|
||||||
(require (planet schematics/random:1:0/random))
|
|
||||||
(random-gaussian)
|
|
||||||
]
|
|
||||||
|
|
||||||
DrRacket automatically downloads version 1.0 of the
|
|
||||||
@filepath{random.plt} library by @filepath{schematics} and then
|
|
||||||
imports the @filepath{random.rkt} module.}
|
|
||||||
|
|
||||||
@item{@margin-note{To save your definitions, use DrRacket's
|
|
||||||
@seclink[#:indirect? #t
|
|
||||||
#:doc '(lib "scribblings/drracket/drracket.scrbl")
|
|
||||||
"menu:file"]{@onscreen{Save Definitions}}
|
|
||||||
menu item.}
|
|
||||||
Some modules live relative to other modules, without
|
|
||||||
necessarily belonging to any particular collection or package.
|
|
||||||
For example, in DrRacket, if you save your definitions so far in a
|
|
||||||
file @filepath{quick.rkt} and add the line
|
|
||||||
|
|
||||||
@racketblock[(provide rainbow square)]
|
|
||||||
|
|
||||||
then you can open a new tab or window in DrRacket, type the new
|
|
||||||
program @filepath{use.rkt} in the same directory as
|
|
||||||
@filepath{quick.rkt}:
|
|
||||||
|
|
||||||
@racketmod[
|
|
||||||
racket
|
|
||||||
(require "quick.rkt")
|
|
||||||
(rainbow (square 5))
|
|
||||||
]
|
|
||||||
|
|
||||||
and when you run @filepath{use.rkt}, a rainbow list of squares
|
|
||||||
is the output. Note that @filepath{use.rkt} is written using
|
|
||||||
the initial import @racketmodname[racket], which does not
|
|
||||||
supply any picture-making functions itself---but does provide
|
|
||||||
@racket[require] and the function-calling syntax.}
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
Racketeers typically write new programs and libraries as modules that
|
|
||||||
import each other through relative paths, and that use existing
|
|
||||||
libraries from collections and @racket[planet]. When a program or
|
|
||||||
library developed this way seems useful to others, it can be uploaded
|
|
||||||
as a @PLaneT package or distributed in the more old-fashioned way as
|
|
||||||
an installable collection archive (in either case without modifying
|
|
||||||
the internal relative references among modules).
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Macros}
|
|
||||||
|
|
||||||
Here's another library to try:
|
|
||||||
|
|
||||||
@mr-def+int[
|
|
||||||
(require slideshow/code)
|
|
||||||
(code (circle 10))
|
|
||||||
]
|
|
||||||
|
|
||||||
Instead of a circle, the result is a picture of the code that, if it
|
|
||||||
were used as an expression, would produce a circle. In other words,
|
|
||||||
@racket[code] is not a function, but instead a new syntactic form for
|
|
||||||
creating pictures; the bit between the opening parenthesis with
|
|
||||||
@racket[code] is not an expression, but instead manipulated by the
|
|
||||||
@racket[code] syntactic form.
|
|
||||||
|
|
||||||
This helps explain what we meant in the previous section when we said
|
|
||||||
that @racketmodname[racket] provides @racket[require] and the
|
|
||||||
function-calling syntax. Libraries are not restricted to exporting
|
|
||||||
values, such as functions; they can also define new syntactic
|
|
||||||
forms. In this sense, Racket isn't exactly a language at all; it's
|
|
||||||
more of an idea for 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
|
|
||||||
@racket[define-syntax] with @racket[syntax-rules]:
|
|
||||||
|
|
||||||
@mr-def+int[
|
|
||||||
(define-syntax pict+code
|
|
||||||
(syntax-rules ()
|
|
||||||
[(pict+code expr)
|
|
||||||
(hc-append 10
|
|
||||||
expr
|
|
||||||
(code expr))]))
|
|
||||||
(pict+code (circle 10))
|
|
||||||
]
|
|
||||||
|
|
||||||
This kind of definition is a macro. The @racket[(pict+code expr)] part
|
|
||||||
is a pattern for uses of the macro; instances of the pattern in a
|
|
||||||
program are replaced by instances of the corresponding template, which
|
|
||||||
is @racket[(hc-append 10 expr (code expr))]. In particular,
|
|
||||||
@racket[(pict+code (circle 10))] matches the pattern with
|
|
||||||
@racket[(circle 10)] as @racket[expr], so it is replaced with
|
|
||||||
@racket[(hc-append 10 (circle 10) (code (circle 10)))].
|
|
||||||
|
|
||||||
Of course, this sort of syntactic extension cuts both ways: inventing
|
|
||||||
a new language can make it easier to say what you want, but harder for
|
|
||||||
others to understand. As it happens, the developers of Racket are
|
|
||||||
constantly giving talks and writing papers that involve Racket code,
|
|
||||||
and it's worthwhile for everyone who works on those products to know
|
|
||||||
about @racket[code].
|
|
||||||
|
|
||||||
In fact, you might want to take a look at the @keep-file["quick.scrbl"]
|
|
||||||
@link["quick.scrbl"]{source of this document}. You'll see that it
|
|
||||||
starts with @racketfont{#lang}, but otherwise doesn't look a lot
|
|
||||||
like Racket; nevertheless, we build this document by running its
|
|
||||||
source as a Racket program. We have to use a lot more than
|
|
||||||
@racket[syntax-rules] to extend Racket's syntax enough for writing
|
|
||||||
documents, but Racket's syntactic extension can take you a long way.
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Objects}
|
|
||||||
|
|
||||||
An object system is another example of a sophisticated language
|
|
||||||
extension that is worth learning and using for Racket users. Objects
|
|
||||||
are sometimes better than functions, even when you have
|
|
||||||
@racket[lambda], and objects work especially well for graphical user
|
|
||||||
interfaces. The API for Racket's GUI and graphics system is expressed
|
|
||||||
in terms of objects and classes.
|
|
||||||
|
|
||||||
The class system itself is implemented by the
|
|
||||||
@racketmodname[racket/class] library, and the
|
|
||||||
@racketmodname[racket/gui/base] library provides the GUI and drawing
|
|
||||||
classes. By convention, the classes are given names that end with
|
|
||||||
@racket[%]:
|
|
||||||
|
|
||||||
@mr-defs+int[
|
|
||||||
[(require racket/class
|
|
||||||
racket/gui/base)
|
|
||||||
(define f (new frame% [label "My Art"]
|
|
||||||
[width 300]
|
|
||||||
[height 300]
|
|
||||||
[alignment '(center center)]))]
|
|
||||||
(send f show #t)
|
|
||||||
]
|
|
||||||
|
|
||||||
@(mr-interaction-eval (send f show #f))
|
|
||||||
|
|
||||||
The @racket[new] form creates an instance of a class, where
|
|
||||||
initialization arguments like @racket[label] and @racket[width] are
|
|
||||||
provided by name. The @racket[send] form calls a method of the object,
|
|
||||||
such as @racket[show], with arguments after the method name; the
|
|
||||||
argument @racket[#t] in this case is the boolean constant ``true.''
|
|
||||||
|
|
||||||
Pictures generated with @racketmodname[slideshow] encapsulate a
|
|
||||||
function that uses the graphics toolbox's drawing commands to render
|
|
||||||
the picture to a drawing context, such as a canvas in a frame. The
|
|
||||||
@racket[make-pict-drawer] function from @racketmodname[slideshow]
|
|
||||||
exposes a picture's drawing function. We can use
|
|
||||||
@racket[make-pict-drawer] in a canvas-painting callback to draw a
|
|
||||||
picture into a canvas:
|
|
||||||
|
|
||||||
@(mr-interaction-eval (require pict/flash))
|
|
||||||
|
|
||||||
@mr-def+int[
|
|
||||||
(define (add-drawing p)
|
|
||||||
(let ([drawer (make-pict-drawer p)])
|
|
||||||
(new canvas% [parent f]
|
|
||||||
[style '(border)]
|
|
||||||
[paint-callback (lambda (self dc)
|
|
||||||
(drawer dc 0 0))])))
|
|
||||||
(add-drawing (pict+code (circle 10)))
|
|
||||||
(add-drawing (colorize (filled-flash 50 30) "yellow"))
|
|
||||||
]
|
|
||||||
|
|
||||||
@centerline{
|
|
||||||
@(mr-interaction-eval-show (scale
|
|
||||||
(bitmap
|
|
||||||
(build-path
|
|
||||||
(collection-path "scribblings/quick")
|
|
||||||
"art.png"))
|
|
||||||
0.5))}
|
|
||||||
|
|
||||||
Each canvas stretches to fill an equal portion of the frame, because
|
|
||||||
that's how a frame manages its children by default.
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
@section{Where to Go From Here}
|
|
||||||
|
|
||||||
This introduction to Racket purposely avoids many of the
|
|
||||||
traditional ways of introducing and distinguishing Lisp or Scheme:
|
|
||||||
prefix arithmetic notation, symbols, quoting and quasiquoting lists,
|
|
||||||
@racket[eval], first-class continuations, and the idea that all syntax
|
|
||||||
is really just a @racket[lambda] in disguise. While those are all part
|
|
||||||
of Racket, they are not the main ingredients of day-to-day programming
|
|
||||||
in Racket.
|
|
||||||
|
|
||||||
Instead, Racket programmers typically program with functions,
|
|
||||||
records, objects, exceptions, regular expressions, modules, and
|
|
||||||
threads. That is, instead of a ``minimalist'' language---which is the
|
|
||||||
way that Scheme is often described---Racket offers a rich language
|
|
||||||
with an extensive set of libraries and tools.
|
|
||||||
|
|
||||||
If you are new to programming or if you have the patience to work
|
|
||||||
through a textbook, we recommend reading
|
|
||||||
@italic{@link["http://www.htdp.org/"]{How to Design Programs}}. If you
|
|
||||||
have already read it, or if you want to see where the book will take
|
|
||||||
you, then see @other-manual['(lib
|
|
||||||
"web-server/scribblings/tutorial/continue.scrbl")].
|
|
||||||
|
|
||||||
For experienced programmers, to continue touring Racket from a
|
|
||||||
systems-oriented perspective instead of pictures, your next stop is
|
|
||||||
@other-manual['(lib "scribblings/more/more.scrbl")].
|
|
||||||
|
|
||||||
To instead start learning about the full Racket language and tools
|
|
||||||
in depth, move on to @other-manual['(lib "guide.scrbl"
|
|
||||||
"scribblings/guide")].
|
|
@ -0,0 +1,68 @@
|
|||||||
|
#lang racket/base
|
||||||
|
(require rackunit)
|
||||||
|
(require "../ptree.rkt" "../world.rkt")
|
||||||
|
|
||||||
|
|
||||||
|
(check-true (pnode? "foo-bar"))
|
||||||
|
(check-true (pnode? "Foo_Bar_0123"))
|
||||||
|
(check-true (pnode? 'foo-bar))
|
||||||
|
(check-true (pnode? "foo-bar.p"))
|
||||||
|
(check-true (pnode? "/Users/MB/foo-bar"))
|
||||||
|
(check-false (pnode? #f))
|
||||||
|
(check-false (pnode? ""))
|
||||||
|
(check-false (pnode? " "))
|
||||||
|
|
||||||
|
(check-true (ptree? '(foo)))
|
||||||
|
(check-true (ptree? '(foo (hee))))
|
||||||
|
(check-true (ptree? '(foo (hee (uncle "foo")))))
|
||||||
|
|
||||||
|
|
||||||
|
(define test-ptree-main `(ptree-main "foo" "bar" (one (two "three"))))
|
||||||
|
(define test-ptree (ptree-root->ptree test-ptree-main))
|
||||||
|
(check-equal? (parent 'three test-ptree) "two")
|
||||||
|
(check-equal? (parent "three" test-ptree) "two")
|
||||||
|
(check-false (parent #f test-ptree))
|
||||||
|
(check-false (parent 'nonexistent-name test-ptree))
|
||||||
|
|
||||||
|
|
||||||
|
(check-equal? (children 'one test-ptree) (list "two"))
|
||||||
|
(check-equal? (children 'two test-ptree) (list "three"))
|
||||||
|
(check-false (children 'three test-ptree))
|
||||||
|
(check-false (children #f test-ptree))
|
||||||
|
(check-false (children 'fooburger test-ptree))
|
||||||
|
|
||||||
|
(check-equal? (siblings 'one test-ptree) '("foo" "bar" "one"))
|
||||||
|
(check-equal? (siblings 'foo test-ptree) '("foo" "bar" "one"))
|
||||||
|
(check-equal? (siblings 'two test-ptree) '("two"))
|
||||||
|
(check-false (siblings #f test-ptree))
|
||||||
|
(check-false (siblings 'invalid-key test-ptree))
|
||||||
|
|
||||||
|
(check-equal? (left-adjacents 'one test-ptree) '("foo" "bar"))
|
||||||
|
(check-equal? (left-adjacents 'three test-ptree) '("foo" "bar" "one" "two"))
|
||||||
|
(check-false (left-adjacents 'foo test-ptree))
|
||||||
|
|
||||||
|
(check-equal? (previous 'one test-ptree) "bar")
|
||||||
|
(check-equal? (previous 'three test-ptree) "two")
|
||||||
|
(check-false (previous 'foo test-ptree))
|
||||||
|
|
||||||
|
(check-equal? (next 'foo test-ptree) "bar")
|
||||||
|
(check-equal? (next 'one test-ptree) "two")
|
||||||
|
(check-false (next 'three test-ptree))
|
||||||
|
|
||||||
|
(check-equal? (ptree->list test-ptree) '("foo" "bar" "one" "two" "three"))
|
||||||
|
|
||||||
|
|
||||||
|
(let ([sample-main `(world:pollen-tree-root-name "foo" "bar" (one (two "three")))])
|
||||||
|
(check-equal? (ptree-root->ptree sample-main)
|
||||||
|
`(world:pollen-tree-root-name "foo" "bar" (one (two "three")))))
|
||||||
|
|
||||||
|
(define files '("foo.html" "bar.html" "bar.html.p" "zap.html" "zap.xml"))
|
||||||
|
(check-equal? (pnode->url/paths 'foo.html files) "foo.html")
|
||||||
|
(check-equal? (pnode->url/paths 'bar.html files) "bar.html")
|
||||||
|
;; (check-equal? (name->url 'zap files) 'error) ;; todo: how to test error?
|
||||||
|
(check-false (pnode->url/paths 'hee files))
|
||||||
|
|
||||||
|
|
||||||
|
(set! test-ptree-main `(,world:ptree-root-node "foo" "bar" (one (two "three"))))
|
||||||
|
(check-equal? (ptree-root->ptree test-ptree-main)
|
||||||
|
`(,world:ptree-root-node "foo" "bar" (one (two "three"))))
|
Loading…
Reference in New Issue