444 lines
14 KiB
Racket
444 lines
14 KiB
Racket
#lang scribble/doc
|
|
@(require scribblings/htdp-langs/common
|
|
scribble/struct
|
|
"std-grammar.ss"
|
|
"prim-ops.ss"
|
|
(for-label deinprogramm/DMdA-beginner))
|
|
|
|
@title[#:style 'toc #:tag "DMdA-beginner"]{Die Macht der Abstraktion - Anfänger}
|
|
|
|
This is documentation for the language level @italic{Die Macht der
|
|
Abstraktion - Anfänger} to go with the German textbook @italic{Die
|
|
Macht der Abstraktion}.
|
|
|
|
@declare-exporting[deinprogramm/DMdA-beginner]
|
|
|
|
@schemegrammar*-DMdA[
|
|
#:literals ()
|
|
() () ()
|
|
]
|
|
|
|
@|prim-nonterms|
|
|
|
|
@prim-ops['(lib "DMdA-beginner.ss" "deinprogramm") #'here]
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{Definitionen}
|
|
|
|
@defform[(define id expr)]{
|
|
Diese Form ist eine Definition, und bindet @scheme[id] als
|
|
globalen Namen an den Wert von @scheme[exp].}
|
|
|
|
@section{Record-Typ-Definitionen}
|
|
|
|
@defform[(define-record-procedures t c p (s1 ...))]{
|
|
|
|
Die @scheme[define-record-procedures]-Form ist eine Definition
|
|
für einen neuen Record-Typ. Dabei ist @scheme[t] der Name der Record-Signatur,
|
|
@scheme[c] der Name des Konstruktors, @scheme[p]
|
|
der Name des Prädikats, und die @scheme[si] sind die
|
|
Namen der Selektoren.}
|
|
|
|
@section[#:tag "application"]{Prozedurapplikation}
|
|
|
|
@defform/none[(expr expr ...)]{
|
|
Dies ist eine Prozeduranwendung oder Applikation.
|
|
Alle @scheme[expr]s werden ausgewertet:
|
|
Der Operator (also der erste Ausdruck) muß eine
|
|
Prozedur ergeben, die genauso viele Argumente
|
|
akzeptieren kann, wie es Operanden, also weitere @scheme[expr]s gibt.
|
|
Die Anwendung wird dann ausgewertet, indem der Rumpf
|
|
der Applikation ausgewertet wird, nachdem die Parameter
|
|
der Prozedur durch die Argumente, also die Werte der
|
|
Operanden ersetzt wurden.}
|
|
|
|
@; @defform[(#%app id expr expr ...)]{
|
|
@;
|
|
@; Eine Prozedurapplikation kann auch mit @scheme[#%app] geschrieben
|
|
@; werden, aber das macht eigentlich niemand.}
|
|
|
|
@section{@scheme[#t] and @scheme[#f]}
|
|
|
|
@as-index{@litchar{#t}} ist das Literal für den booleschen Wert "wahr",
|
|
@as-index{@litchar{#f}} das Literal für den booleschen Wert "falsch".
|
|
|
|
@section{@scheme[lambda]}
|
|
|
|
@defform[(lambda (id ...) expr)]{
|
|
Ein Lambda-Ausdruck ergibt bei der Auswertung eine neue Prozedur.}
|
|
|
|
@section[#:tag "id"]{Bezeichner}
|
|
|
|
@defform/none[id]{
|
|
Eine Variable bezieht sich auf die, von innen nach
|
|
außen suchend, nächstgelegene Bindung durch @scheme[lambda], @scheme[let], @scheme[letrec], oder
|
|
@scheme[let*]. Falls es keine solche lokale Bindung gibt, muß es eine
|
|
Definition oder eine eingebaute Bindung mit dem entsprechenden Namen
|
|
geben. Die Auswertung des Namens ergibt dann den entsprechenden
|
|
Wert. }
|
|
|
|
@section{@scheme[cond]}
|
|
|
|
@defform[(cond (expr expr) ... (expr expr))]{
|
|
Ein @scheme[cond]-Ausdruck bildet eine Verzweigung, die aus mehreren
|
|
Zweigen besteht. Jeder Zweig besteht
|
|
aus einem Test und einem Ausdruck. Bei der Auswertung werden die
|
|
Zweige nacheinander abgearbeitet. Dabei wird jeweils zunächst der Test
|
|
ausgewertet, der jeweils einen booleschen Wert ergeben müssen. Beim
|
|
ersten Test, der @scheme[#t] ergibt, wird der Wert des Ausdrucks des Zweigs zum
|
|
Wert der gesamten Verzweigung. Wenn kein Test @scheme[#t] ergibt, wird das
|
|
Programm mit einer Fehlermeldung abgebrochen.
|
|
}
|
|
|
|
@defform/none[#:literals (cond else)
|
|
(cond (expr expr) ... (else expr))]{
|
|
Die Form des cond-Ausdrucks ist ähnlich zur vorigen, mit der
|
|
Ausnahme, daß in dem Fall, in dem kein Test @scheme[#t] ergibt, der Wert des
|
|
letzten Ausdruck zum Wert der @scheme[cond]-Form wird.
|
|
}
|
|
|
|
@defidform[else]{
|
|
|
|
Das Schlüsselwort @scheme[else] kann nur in @scheme[cond] benutzt werden.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[if]}
|
|
|
|
@defform[(if expr expr expr)]{
|
|
Eine @scheme[if]-Form ist eine binäre Verzweigung. Bei der Auswertung wird
|
|
zunächst der erste Operand ausgewertet (der Test), der einen
|
|
booleschen Wert ergeben muß. Ergibt er @scheme[#t], wird der Wert des zweiten
|
|
Operanden (die Konsequente) zum Wert der @scheme[if]-Form, bei @scheme[#f] der Wert des
|
|
dritten Operanden (die Alternative).
|
|
}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[and]}
|
|
|
|
@defform[(and expr ...)]{
|
|
Bei der Auswertung eines @scheme[and]-Ausdrucks werden nacheinander die
|
|
Operanden (die boolesche Werte ergeben müssen) ausgewertet. Ergibt
|
|
einer @scheme[#f], ergibt auch der and-Ausdruck @scheme[#f]; wenn alle
|
|
Operanden @scheme[#t] ergeben, ergibt auch der @scheme[and]-Ausdruck
|
|
@scheme[#t].
|
|
}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{@scheme[or]}
|
|
|
|
@defform[(or expr ...)]{
|
|
Bei der Auswertung eines @scheme[or]-Ausdrucks werden nacheinander die
|
|
Operanden (die boolesche Werte ergeben müssen) ausgewertet. Ergibt
|
|
einer @scheme[#t], ergibt auch der or-Ausdruck @scheme[#t]; wenn alle Operanden @scheme[#f]
|
|
ergeben, ergibt auch der @scheme[or]-Ausdruck @scheme[#f].
|
|
}
|
|
|
|
@section{Signaturen}
|
|
|
|
Signaturen können statt der Verträge aus dem Buch geschrieben werden:
|
|
Während Verträge reine Kommentare sind, überprüft DrRacket Signaturen
|
|
und meldet etwaige Verletzungen.
|
|
|
|
@subsection{@scheme[signature]}
|
|
@defform[(signature sig)]{
|
|
Diese Form liefert die Signatur mit der Notation @scheme[sig].
|
|
}
|
|
|
|
@subsection{Signaturdeklaration}
|
|
@defform[(: id sig)]{
|
|
Diese Form erklärt @scheme[sig] zur gültigen Signatur für @scheme[id].
|
|
}
|
|
|
|
@subsection{Eingebaute Signaturen}
|
|
|
|
@defidform[number]{
|
|
Signatur für beliebige Zahlen.
|
|
}
|
|
|
|
@defidform[real]{
|
|
Signatur für reelle Zahlen.
|
|
}
|
|
|
|
@defidform[rational]{
|
|
Signatur für rationale Zahlen.
|
|
}
|
|
|
|
@defidform[integer]{
|
|
Signatur für ganze Zahlen.
|
|
}
|
|
|
|
@defidform[natural]{
|
|
Signatur für ganze, nichtnegative Zahlen.
|
|
}
|
|
|
|
@defidform[boolean]{
|
|
Signatur für boolesche Werte.
|
|
}
|
|
|
|
@defidform[true]{
|
|
Signatur für \scheme[#t].
|
|
}
|
|
|
|
@defidform[false]{
|
|
Signatur für \scheme[#f].
|
|
}
|
|
|
|
@defidform[string]{
|
|
Signatur für Zeichenketten.
|
|
}
|
|
|
|
@defidform[empty-list]{
|
|
Signatur für die leere Liste.
|
|
}
|
|
|
|
@defidform[any]{
|
|
Signatur, die auf alle Werte gültig ist.}
|
|
|
|
@defform/none[signature]{
|
|
Signatur für Signaturen.}
|
|
|
|
@defidform[property]{
|
|
Signatur für Eigenschaften.}
|
|
|
|
@subsection{@scheme[predicate]}
|
|
@defform[(predicate expr)]{
|
|
Bei dieser Signatur muß @scheme[expr] als Wert ein Prädikat haben, also
|
|
eine Prozedur, die einen beliebigen Wert akzeptiert und entweder @scheme[#t]
|
|
oder @scheme[#f] zurückgibt.
|
|
Die Signatur ist dann für einen Wert gültig, wenn das Prädikat, darauf angewendet,
|
|
@scheme[#t] ergibt.
|
|
}
|
|
|
|
@subsection{@scheme[one-of]}
|
|
@defform[(one-of expr ...)]{
|
|
Diese Signatur ist für einen Wert gültig, wenn er gleich dem Wert eines
|
|
der @scheme[expr] ist.
|
|
}
|
|
|
|
@subsection{@scheme[mixed]}
|
|
@defform[(mixed sig ...)]{
|
|
Diese Signatur ist für einen Wert gültig, wenn er für eine der Signaturen
|
|
@scheme[sig] gültig ist.
|
|
}
|
|
|
|
@subsection[#:tag "proc-signature"]{Prozedur-Signatur}
|
|
@defidform[->]{
|
|
@defform/none[(sig ... -> sig)]{
|
|
Diese Signatur ist dann für einen Wert gültig, wenn dieser eine
|
|
Prozedur ist. Er erklärt außerdem, daß die Signaturen vor dem @scheme[->]
|
|
für die Argumente der Prozedur gelten und die Signatur nach dem @scheme[->]
|
|
für den Rückgabewert.
|
|
}}
|
|
}
|
|
|
|
@subsection[#:tag "signature-variable"]{Signatur-Variablen}
|
|
@defform/none[%a]
|
|
@defform/none[%b]
|
|
@defform/none[%c]
|
|
@defform/none[...]{
|
|
Dies ist eine Signaturvariable: sie steht für eine Signatur, die für jeden Wert gültig ist.
|
|
}
|
|
|
|
@subsection{@scheme[combined]}
|
|
@defform[(combined sig ...)]{
|
|
Diese Signatur ist für einen Wert gültig, wenn sie für alle der Signaturen
|
|
@scheme[sig] gültig ist.
|
|
}
|
|
|
|
@section{Testfälle}
|
|
|
|
@defform[(check-expect expr expr)]{
|
|
|
|
Dieser Testfall überprüft, ob der erste @scheme[expr] den gleichen
|
|
Wert hat wie der zweite @scheme[expr], wobei das zweite @scheme[expr]
|
|
meist ein Literal ist.}
|
|
|
|
@defform[(check-within expr expr expr)]{
|
|
|
|
Wie @scheme[check-expect], aber mit einem weiteren Ausdruck,
|
|
der als Wert eine Zahl @scheme[_delta] hat. Der Testfall überprüft, daß jede Zahl im Resultat
|
|
des ersten @scheme[expr] maximal um @scheme[_delta]
|
|
von der entsprechenden Zahl im zweiten @scheme[expr] abweicht.}
|
|
|
|
@defform[(check-member-of expr expr ...)]{
|
|
|
|
Ähnlich wie @scheme[check-expect]: Der Testfall überprüft, daß das Resultat
|
|
des ersten Operanden gleich dem Wert eines der folgenden Operanden ist.}
|
|
|
|
@defform[(check-range expr expr expr)]{
|
|
|
|
Ähnlich wie @scheme[check-expect]: Alle drei Operanden müssen
|
|
Zahlen sein. Der Testfall überprüft, ob die erste Zahl zwischen der
|
|
zweiten und der dritten liegt (inklusive).}
|
|
|
|
@defform[(check-error expr expr)]{
|
|
|
|
Dieser Testfall überprüft, ob der erste @scheme[expr] einen Fehler produziert,
|
|
wobei die Fehlermeldung der Zeichenkette entspricht, die der Wert des zweiten
|
|
@scheme[expr] ist.}
|
|
|
|
@defform[(check-property expr)]{
|
|
|
|
Dieser Testfall überprüft experimentell, ob die @tech{Eigenschaft}
|
|
@scheme[expr] erfüllt ist. Dazu werden zufällige Werte für die mit
|
|
@scheme[for-all] quantifizierten Variablen eingesetzt: Damit wird
|
|
überprüft, ob die Bedingung gilt.
|
|
|
|
@emph{Wichtig:} @scheme[check-property] funktioniert nur für
|
|
Eigenschaften, bei denen aus den Signaturen sinnvoll Werte generiert
|
|
werden können. Dies ist für die meisten eingebauten Signaturen der
|
|
Fall, aber nicht für Signaturvariablen und Signaturen, die mit @scheme[predicate]
|
|
erzeugt wurden. In diesen Fällen erzeugt @scheme[check-property] eine Fehlermeldung.
|
|
}
|
|
|
|
@section{Parametrische Record-Typ-Definitionen}
|
|
|
|
@defform[(define-record-procedures-parametric t cc c p (s1 ...))]{
|
|
|
|
Die @scheme[define-record-procedures-parametric] ist wie
|
|
@scheme[define-record-procedures]. Zusäzlich wird der Bezeichner
|
|
@scheme[cc] an einen Signaturkonstruktor gebunden: Dieser akzeptiert
|
|
für jedes Feld eine Feld-Signatur und liefert eine Signatur, die nur
|
|
Records des Record-Typs @scheme[t] erfüllen, bei dem die Feldinhalte
|
|
die Feld-Signaturen erfüllen.
|
|
|
|
Beispiel:
|
|
|
|
@schemeblock[
|
|
(define-record-procedures-parametric pare pare-of
|
|
make-pare pare?
|
|
(pare-one pare-two))
|
|
]
|
|
|
|
Dann ist @scheme[(pare-of integer string)] die Signatur für
|
|
@scheme[pare]-Records, bei dem die Feldinhalte die Signaturen
|
|
@scheme[integer] bzw. @scheme[string] erfüllen müssen.
|
|
|
|
Die Signaturen für die Feldinhalte werden erst überprüft, wenn ein
|
|
Selektor aufgerufen wird.
|
|
}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@; @section{@scheme[require]}
|
|
@;
|
|
@; @defform[(require string)]{
|
|
@;
|
|
@; Diese Form macht die Definitionen des durch @scheme[string] spezifizierten Moduls
|
|
@; verfügbar. Dabei bezieht sich @scheme[string] auf eine Datei relativ zu der Datei,
|
|
@; in der die @scheme[require]-Form steht.
|
|
@;
|
|
@; Dabei ist @scheme[string] leicht eingeschränkt, um Portabilitätsprobleme zu vermeiden:
|
|
@; @litchar{/} ist der Separator für Unterverzeichnisse,, @litchar{.} bedeutet das aktuelle
|
|
@; Verzeichnis, @litchar{..} meint das übergeordnete Verzeichnis, Pfadelemente
|
|
@; können nur @litchar{a} bis @litchar{z} (groß oder klein),
|
|
@; @litchar{0} bis @litchar{9}, @litchar{-}, @litchar{_}
|
|
@; und @litchar{.} enthalten, und die Zeichenkette kann nicht leer sein oder
|
|
@; ein @litchar{/} am Anfang oder Ende enthalten.}
|
|
@;
|
|
@;
|
|
@; @defform/none[#:literals (require)
|
|
@; (require module-id)]{
|
|
@;
|
|
@; Diese Form macht eine eingebaute Library mit dem Namen @scheme[module-id] verfügbar.}
|
|
@;
|
|
@; @defform/none[#:literals (require lib)
|
|
@; (require (lib string string ...))]{
|
|
@;
|
|
@; Diese Form macht die Definitionen eines Moduls in einer installierten Bibliothek
|
|
@; verfügbar.
|
|
@; Der erste
|
|
@; @scheme[string] ist der Name der Datei des Moduls, und die restlichen
|
|
@; @scheme[string]s bezeichnen die Collection (und Sub-Collection undsoweiter),
|
|
@; in der die Datei installiert ist. Jede @scheme[string] ist ebenso eingeschränkt
|
|
@; wie bei @scheme[(require string)].}
|
|
@;
|
|
@;
|
|
@; @defform/none[#:literals (require planet)
|
|
@; (require (planet string (string string number number)))]{
|
|
@;
|
|
@; Diese Form macht ein Modul einer Bibliothek verfügbar, die aus PLaneT
|
|
@; kommt.}
|
|
|
|
@; ----------------------------------------
|
|
|
|
@section{Eigenschaften}
|
|
|
|
Eine @deftech{Eigenschaft} definiert eine Aussage über einen
|
|
Scheme-Ausdruck, die experimentell überprüft werden kann. Der
|
|
einfachste Fall einer Eigenschaft ist ein boolescher Ausdruck. Die
|
|
folgende Eigenschaft gilt immer:
|
|
|
|
@schemeblock[
|
|
(= 1 1)
|
|
]
|
|
|
|
Es ist auch möglich, in einer Eigenschaft Variablen zu verwenden, für
|
|
die verschiedene Werte eingesetzt werden. Dafür müssen die Variablen
|
|
gebunden und @deftech{quantifiziert} werden, d.h. es muß festgelegt
|
|
werden, welche Signatur die Werte der Variable erfüllen sollen.
|
|
Eigenschaften mit Variablen werden mit der @scheme[for-all]-Form erzeugt:
|
|
|
|
@defform[(for-all ((id sig) ...) expr)]{
|
|
Dies bindet die Variablen @scheme[id] in der Eigenschaft
|
|
@scheme[expr]. Zu jeder Variable gehört eine Signatur
|
|
@scheme[sig], der von den Werten der Variable erfüllt werden
|
|
muß.
|
|
|
|
Beispiel:
|
|
|
|
@schemeblock[
|
|
(for-all ((x integer))
|
|
(= x (/ (* x 2) 2)))
|
|
]
|
|
}
|
|
|
|
@defform[(expect expr expr)]{
|
|
|
|
Ein @scheme[expect]-Ausdruck ergibt eine Eigenschaft, die dann gilt,
|
|
wenn die Werte von @scheme[expr] und @scheme[expr] gleich sind, im
|
|
gleichen Sinne wie bei @scheme[check-expect].}
|
|
|
|
|
|
@defform[(expect-within expr expr expr)]{
|
|
|
|
Wie @scheme[expect], aber entsprechend @scheme[check-within] mit einem
|
|
weiteren Ausdruck, der als Wert eine Zahl @scheme[_delta] hat. Die
|
|
resultierende Eigenschaft gilt, wenn jede Zahl im Resultat des ersten
|
|
@scheme[expr] maximal um @scheme[_delta] von der entsprechenden Zahl
|
|
im zweiten @scheme[expr] abweicht.}
|
|
|
|
@defform[(expect-member-of expr expr ...)]{
|
|
|
|
Wie @scheme[expect], aber entsprechend @scheme[check-member-of] mit
|
|
weiteren Ausdrücken, die mit dem ersten verglichen werden. Die
|
|
resultierende Eigenschaft gilt, wenn das erste Argument gleich
|
|
einem der anderen Argumente ist.}
|
|
|
|
@defform[(expect-range expr expr expr)]{
|
|
|
|
Wie @scheme[expect], aber entsprechend @scheme[check-range]: Die
|
|
Argumente müssen Zahlen sein. Die Eigenschaft gilt, wenn die erste Zahl
|
|
zwischen der zweiten und dritten Zahl liegt (inklusive).}
|
|
|
|
|
|
@defform[(==> expr expr)]{
|
|
Der erste Operand ist ein boolescher Ausdruck, der zweite Operand eine
|
|
Eigenschaft: @scheme[(==> c p)] legt fest, daß die Eigenschaft
|
|
@scheme[p] nur erfüllt sein muß, wenn @scheme[c] (die
|
|
@emph{Bedingung}) @scheme[#t] ergibt, also erfüllt ist.}
|
|
|
|
@schemeblock[
|
|
(for-all ((x integer))
|
|
(==> (even? x)
|
|
(= x (* 2 (/ x 2)))))
|
|
]
|
|
|
|
@section[#:tag "beginner-prim-ops"]{Primitive Operationen}
|
|
|
|
@prim-op-defns['(lib "DMdA-beginner.ss" "deinprogramm") #'here '()]
|