The Making of Scheme in Perl. This
document is linked from Wolf Busch's Heimatseite, and it's completely German. If you know one who
understands German very good, ask him to translate it. If you
understand German yourself
Vorwort |
\bersicht |
Ungefdhr 1976 las ich in der Zeitschrift "Spektrum der Wissenschaft", Rubrik "Mathematische Knobeleien", einen Artikel von Douglas R. Hofstadter, der dem geneigten Leser auf spielerische Art die Programmiersprache LISP beibrachte. Ich war geneigt. Mit dem Bleistift und reichlich unbeholfen lvste ich die \bungsaufgaben. Und unbeleckt von jeglichem Vorwissen erkundigte ich mich sofort bei einem Thekennachbarn, Studiengang Elektrotechnik, nach einem programmierbaren Taschenrechner mit LISP
In der Dra der Homecomputer erschien eine LISP-Implementierung f|r das Wohnzimmerwunder C64. 1987 kaufte ich meinen ersten XT f|r sensationelle 2 Mille, Monitor gr|n, und selbstverstdndlich kam Xlisp drauf, preisg|nstiger gehts nimmer. 1990 leistete ich mir eine legale Lizenz f|r PC-Scheme von Texas Instruments und stellte fest: Scheme ist mein Lieblingslisp. Denn es ist fix und vor allem einfach!
Heute gibt es Perl5 als beinahe plattformunabhdngige
Skriptsprache. Lduft |berall, kennt kaum Speichergrenzen, kann
Daten referenzieren, ist umsonst, und vor allem: es macht
ungefragt einen Garbage Collect, wenn Not im Speicher
ist. Ein Perl-Programm ist nach Aussage des
Autors Larry Wall in der Laufzeit langsamer, aber in der
Entwicklung schneller als C.
Setzen wir einen drauf und sagen: Ein LISP-Programm ist in der
Laufzeit langsamer, aber in der Entwicklung schneller als Perl,
und begeben uns an die Feierabendaufgabe, LISP in Perl zu
implementieren. (Nichts gegen Perl. Es ist durch und durch prima.
Aber warum nicht beides?)
Dieser Text dient als Begleitlekt|re f|r den, der den Quelltext liest oder eigene Erweiterungen vornehmen will. Er ist auch f|r den interessierte Laien geeignet, der reinschnuppern will. Zum Verstdndnis brauchen Sie, lieber Leser, etwas Vorkenntnis |ber die Speicherverwaltung von LISP und Sie sollten ein bi_chen Perl kvnnen.
Wir gehen nach der Strategie Bottom-Top vor. Wir beschrdnken uns auf die dokumentierten Fdhigkeiten von Perl wir vermeiden unverstdndlichen Hacks. Einen Knoten realisieren wir z. B. mit einer Referenz auf ein Feld mit zwei Elementen. Im Dokument perlref.html erfahren Sie, wie Referenzen funktionieren.
Zum zitierten Quelltext in diesem Artikel: Sie finden hier eine verstdndlich aufbereitete Fassung der einzelnen Funktionen vor, die dem Verstdndnis zugute kommt. Im Quelltext steht meist eine Kurzfassung, die zugunsten des Durchsatzes auf private Variablen verzichtet, sich aber nicht so fl|ssig liest.
Die Shell, Anatomie eines Knotens, Daten und Typen, Listen, Einlesen der Benutzereingabe, Ausdr|cke evaluieren, Spezialformen, Primitive Funktionen, Makros, Zusammenfassung, Erweiterungen, Was der Meister sich verkniff, Ausblick; Erweiterungen 1.1, Bekannte Fehler.
Anhang: Technik, Besonderheiten, Makros, Restriktionen, Spezialformen, Perl, Funktionen, Glossar.
Beim Entwickeln verwenden wir Projekt-Shell.pl, so da_ wir die Zwischenresultate interaktiv kontrollieren kvnnen:
$prompt = "Perl> ";
print $prompt;
do { my($commandline);
my @result = eval ($commandline);
print "=> " . (join (", ", @result)) . ";" ;
print $@; # Fehlerstatus
print "\n$prompt";
} until eof;
Die Sorte von Shell kennen wir bereits von LISP. Sie liest
eine Eingabezeile, schickt sie durch die Routine eval()
und beantwortet unsere Anfrage ohne Umschweife in der ndchsten
Zeile.
LISP arbeitet mit Knoten. Perl arbeitet auf Wunsch mit Referenzen (symbolischen Zeigern) auf allerlei Daten. Einen LISP-Knoten realisieren wir in Perl mit einer Referenz auf ein Feld (Array), das zwei Elemente hat. Element 1 ist das CAR, Element 2 das CDR. Beide Elemente m|ssen Skalare sein ("einfache" Werte, also eine Zahl, ein Wort, eine Referenz. Felder und Assoziativlisten scheiden aus.)
So bilden wir einen Primitivknoten:
sub ConsLowLevel {
my $car = shift();
my $cdr = shift();
my @pair = ($car, $cdr);
my $reference = \@pair;
return($reference) };
Die privaten Skalare $car und $cdr
nehmen die Funktionsargumente auf. Das private Feld @pair
erhdlt die Skalare $car und $cdr als
Elemente. Der private Skalar $reference nimmt die
Referenz auf @pair auf. Die Funktion |bergibt als
R|ckgabewert diese Referenz. Den gleichen Effekt hat die
k|rzere Version , die ohne
private Variablen auskommt. Welche Version wir verwenden, ist
Geschmackssache.
Und so sieht die Funktion aus, die den Primitiv-CAR ausliest:
sub CarLowLevel {
my $reference = shift();
my @pair = @$reference;
my ($car, $cdr) = @pair;
return $car };
Im Quelltext node.pl finden Sie die didaktisch nicht ganz so
gehaltvolle Kurzfassung ,
deren Analyse wir uns hier schenken. Wer sie kryptisch findet,
sei auf die Perl-Dokumentation verwiesen, insbesondere
perllol.html.
In Pascal z. B. sind die Datentypen mit den Variablen verbunden, in LISP nicht. Deswegen ist LISP aber nicht typfrei, sondern verbindet die Datentypen direkt mit den Werten. Wir realisieren ein Datum als Primitivknoten, dessen CAR den Typ und dessen CDR den Wert des Datums enthdlt. Damit kann eine Rechenoperation zur Laufzeit |berpr|fen, ob sie auch die richtige Futtersorte hat.
Die Datentypen Symbol, Pair, werden durchnumeriert mit 1, 2, , und damit bauen wir die Funktionen, welche die Daten mitsamt Typ erzeugen:
sub MakeSymbol { &ConsLowLevel (1, @_) } ;
sub MakePair { &ConsLowLevel (2, @_) } ;
# (
)
Die Typ-Kontrolle erfolgt dementsprechend mit Abfrage des Primitiv-CAR:
sub IsSymbol { &CarLowLevel (@_) == 1 } ;
sub IsPair { &CarLowLevel (@_) == 2 } ;
# (
)
Die Wertabfrage des Datums geschieht entsprechend:
sub GetValue { &CdrLowLevel (@_) }
Was immer der Wert des Datums ist, er steht im CDR des Primitivknotens sei es ein Skalar, dann darf er kvrperlich drin sein, oder sei es eine komplexe Datenstruktur, dann mu_ es eine Referenz darauf sein. Beim Datentyp Vektor z. B. ist der Wert des Datums eine Referenz auf ein Perl-Datenfeld.
Wir brauchen hier noch nicht zu bestimmen, wie die Datentypen im einzelnen zu handhaben sind sie sind jederzeit feststellbar, und kvnnen wir die einzelnen Fdlle spdter behandeln.
$apfel = &MakeSymbol("APPLE");
$NIL = &MakeBoolean("");
&IsBoolean($NIL) ` 1
Stop. Der Revised4 Report sagt aus, da_
die Leere Liste nil nicht zu "falsch"
evaluieren mu_, sondern je nach Implementierung auch als
"wahr" gewertet werden darf, und weiter: NIL
ist nicht vom Typ boolean. Das tut weh. Ich bleibe bei
"falsch", bitte mich nicht pr|geln.
Der fundamentale Typ von zusammengesetzten Daten ist die
Liste. Sie besteht aus einer Verkettung von Knoten. Das letzte
Element ist die Leere Liste nil. Zusammengesetzt
wird ein Knoten mit der Funktion &CONS(), die
ein LISP-Datum des Typs PAIR erzeugt. Im Quelltext pair.pl finden
Sie die Kurzfassung des Perl-Codes, hier die etwas
verstdndlichere Langfassung:
sub CONS {
my $car = shift();
my $cdr = shift();
my $node = &ConsLowLevel($car,$cdr);
my $pair = &MakePair($node);
return $pair };
Dazu die Funktion:
sub CAR {
my $pair = shift();
my $node = &GetValue($pair);
return &CarLowLevel($node) };
und dementsprechend die Funktion &CDR(), und
ein Grundstock f|r die Listenarithmetik ist gelegt na ja,
noch nicht ganz. Was soll passieren, wenn wir aus Versehen die
Funktion &CAR() auf ein Symbol anwenden,
z. B. $apfel (` APPLE)?
Jawohl, genau richtig, eine Fehlermeldung!
sub CAR {
my $pair = shift();
&IsPair($pair) || die (&TypeMismatchMessage("CAR", "PAIR", $pair));
my $node = &GetValue($pair);
return &CarLowLevel($node) };
Die Perl-Funktion die() ist hier die Ultima
Ratio: Datentyp falsch will tot umfallen. Und weil das zu
den Hauptfehlerquellen zdhlt, lohnt die Funktion &TypeMismatchMessage(),
die den Absturz in simpler englischer Grammatik begr|ndet,
z. B. .
Sie ruft ihrerseits die Funktion &PrintForm()
auf, die das LISP-Datum ins Menschliche |bersetzt.
\brigens sind Listen teilbar (genau das tun die Funktionen CAR
und CDR). Alles andere gilt als unteilbar. Darum
gilt in LISP alles, was nicht Liste ist, als Atom.
Die klassische Benutzerschnittstelle von LISP ist die Programmschleife, welche die Eingabe vom Benutzer einliest, berechnet und ausgibt (read-eval-print-loop, abgek|rzt REP, hat nichts zu tun mit irgendwelchen politisch kackfarbenen Umtrieben.)
Als Grundlage brauchen wir einen dazu Parser, der aus einem |bergebenen Text ein LISP-Datum macht. Das geschieht mit Mustervergleich. Perl fdhrt dabei zu Hvchstform auf.
Trotzdem m|ssen wir ein kleines bi_chen "um die Ecke" denken. Falls der Parser sich rekursiv selbst aufrufen mu_, mu_ er stdndig auf dem Laufenden sein |ber das aktuelle Zwischenresultat (der abgearbeiteten Zeichenkette) und den Rest. Also |bergibt der Parser als Resultat ein Feld, dessen erstes Element das teilberechnete LISP-Datum ist; das zweite Element enthdlt den Rest der Kommandozeile, der noch abzuarbeiten bleibt. (Perl pur, diesmal keine Knoten!)
Der Parser erkennt aus der Benutzereingabe die Datentypen Buchstabe, Wort, Zahl, Wahrheitswert, Symbol, Vektor und Liste. Die |brigen Datentypen sind nicht unmittelbar einzugeben, sondern entstehen bei Berechnungen in LISP.
sub ParseAtom {
my $str = shift;
my $port = shift;
my @result;
($str, $port) = &input($str, $port);
$str =~ s/^\s*//; # f|hrende Leerzeichen entfernen
if ($str eq '' && $port eq 'eof') { @result = ($EOF, $str) } # eof
elsif ( $str =~ /^(#e|#i)?$NumberPattern/io ) { # Number
my @arr = &ParseNumArr($&);
@result = (&MakeNumber(\@arr), $') }
#
Buchstabe, Wort,
elsif ($str =~ s/^'//) { # quote
my ($qval, $rest) = &ParseAtom($str);
my $first = &MakeList(&MakeSymbol("QUOTE"), $qval);
@result = ($first, $rest) }
#
Quasiquote, Unquote, Unquote-Splicing
elsif ($str =~ s/^\(//) { # Listen
@result = &ParseList($str) }
else { die "Don't understand input '$str'"}; # sonst stimmt was nicht.
@result }
Der Parser ist |brigens die Instanz, die das 'Hochkomma
als Abk|rzung f|r behandelt,
ebenso das umgekehrte Hochkomma f|r ,
das Komma f|r und das Komma
mit Klammeraffen f|r .
Die Funktion &ParseAtom() erhdlt als
Argument die Benutzereingabe und den Port. Falls die Eingabe sich
|ber mehrere Zeilen erstreckt (bei Listen und Vektoren), f|llt
sie mit $input() die Zeile wieder auf.
Die Funktion &ParseList() macht aus
Klammerausdr|cken Listen. Dabei ruft sie f|r die atomaren
Elemente wiederum &ParseAtom() auf. Sie
unterscheidet zwischen "ordentlichen" und
"unordentlichen" Listen (solchen, deren innerstes CDR
nicht nil ist), bietet aber ansonsten nicht viel
neues und ist deshalb hier ausgelassen.
Oben habe ich gesagt, da_ wir uns erst bei Bedarf um die Behandlung der einzelnen Datentypen zu k|mmern brauchen. Sie d|rfen mich beim Wort nehmen, jetzt sind die Zahlen dran. Das Zahlen-Vergleichsmuster fdllt etwas ldnglich aus und wird deshalb aus mehreren Teilmustern zusammengesetzt, die letztlich die Standard-Zahleneingaben erkennen und f|r LISP aufbereiten.
Der Scheme-Standard sieht vor, da_ die Zahlen genau und ungenau sein d|rfen. Wir stellen eine Zahl als Referenz auf ein Feld dar, dessen erstes Element den Zdhler und dessen zweites den Nenner der Zahl enthdlt. Weiter: der Standard sieht komplexe Zahlen vor. Das Feld enthdlt also weitere zwei entsprechende Eintrdge f|r den imagindren Anteil.
Wenn wir also eine Zahl in der Form 3/4+2/5i
eingeben, ruft der Parser unterm Strich die Funktion auf. Die Zahl in diesem Beispiel ist genau.
Ungenaue Zahlen kennzeichnen wir durch den Nenner 0,
z. B. . Das
ist verwechslungssicher, denn bekanntlich darf nicht durch 0
dividiert werden.
Um die Genauigkeit durch mvglichst viele Rechenoperationen
hindurch zu retten, mu_ unser LISP den Bruch bei Bedarf k|rzen.
Um z. B. die Zahl 48/56 darzustellen, ermittelt die
Perl-Funktion deren grv_ten
gemeinsamen Teiler, also 8, und wir k|rzen damit zu 6/7.
Die interne Zahlengenauigkeit in meinem verwendeten Perl ist
md_ig. Als Kriterium f|r Genauigkeit verwende ich den
Mustervergleich auf den Buchstaben e im Zahlwort (in Perl ist es
Wurscht, ob eine Zahl als "richtige" Zahl oder als Wort
eingesetzt ist.) Falls also der Buchstabe e
auftaucht, gilt die Zahl als ungenau. Falls z. B. die
Funktion darauf stv_t, warnt
sie:
und tut es auch. Was solls.
Intern rechnet dies LISP mit der vollen verf|gbaren
Zahlengenauigkeit, aber bei der Bildschirmdarstellung der Zahlen
wird die 14. Nachkommastelle kaufmdnnisch gerundet, so da_ die
Funktion auf dem Bildschirm 0.0
produziert, obwohl es bei meinem Perl intern -1.22514845490862e-16
sind. Wenn die Rechenungenauigkeit genau wissen wollen, verwenden
Sie die Funktion (number->string). Wenn Sie die
volle Wahrheit auf dem Bildschirm haben wollen,
entfernen Sie in der Funktion &NumberPrintForm()
den Funktionsaufruf &ungefaehr() (siehe
Quelltext pair.pl). Ach so, Kosinus und PI sind in diesem
LISP nicht drin, aber Sie kvnnen es definieren, das Werkzeug ist
da.
Wir haben die fundamentalen LISP-Funktionen &CONS(),
&CAR(), &CDR()ganz normal in
Perl programmiert. Das ld_t vermuten, da_ Perl von sich aus
imstande ist, LISP-Programme auszuf|hren. Die Vermutung stimmt.
Die Unterschiede sind rein syntaktischer Natur, und sie lassen
sich an einer Hand aufzdhlen.
Die Funktion &GeneratePerlSource($expr) macht
aus einem LISP-Ausdruck einen Perl-Quelltext, den der Evaluierer &EvalExpr()der
Perl-Funktion eval() zuf|hrt. Die Details:
LISP-Symbole schvpfen aus einem grv_eren Zeichenvorrat als
Perl-Variablen. Perl kann zwar auch Variablennamen aus
Sonderzeichen zusammenstellen, z. B. ${'---'},
aber nicht innerhalb von privaten Umgebungen via my dort geht es
strikt alphanumerisch zu. Also ersetzen wir jedes Sonderzeichen
durch seinen ASCII-Wert, dreistellig, mit f|hrendem Unterstrich.
Zur Vermeidung von Kollisionen erhdlt jeder Variablenname
zusdtzlich einen f|hrenden Unterstrich. Mit der Funktion &Lisp2PerlName("+")
erzeugt der Quelltextgenerator z. B. aus dem Symbol + den Variablennamen ${__043}.
Wenn eine Perl-Variable noch nicht definiert ist, dann erzeugt
sie den Undefinierten Wert. Wenn ein LISP-Symbol noch nicht
definiert ist, setzt es eine Fehlermeldung. Bei Verwendung des
Symbols PI mu_ der Quelltext also lauten: . Das Programmteil, die den
Quelltext erzeugt, lautet also:
{ my $name = &GetSymbolName($expr);
my $Pname = &Lisp2PerlName($name);
"($Pname || die ('unbound: $name')" }
Falls ein Symbol sicher existiert, also als formales Argument innerhalb eines LABDA-Ausdrucks, entfdllt die Pr|fung. Siehe eval.pl und special.pl.
Eine Konstante stellen wir im Quelltext dar wie in "Daten
und Typen" beschrieben: als Referenz auf ein anonymes Feld,
das den Typ und den Wert der Konstanten enthdlt. Das Wort "a+b"
steht im Quelltext als ,
sicherheitshalber entschdrft mit quotemeta("a+b"),
die Zahl 1 als . Der Programmteil
f|r Worte und Buchstaben lautet ,
der f|r Zahlen .
Ein LISP-Ausdruck kann auch eine Konstante enthalten, die nur
aus Berechnungen entstehen kann, z. B. eine zyklische Liste.
Wie kriegen wir diese in einen Quelltext? Wir schreiben ihre
Referenz in die Assoziativliste %Val und schreiben
den Schl|ssel in den Quelltext. Als Schl|ssel nehmen wir die
Bildschirmreprdsentation der Referenz, z. B. ARRAY(0x796b20).
Die Funktion &GenerateValueSource() tut das und
|bergibt z. B. den Quelltext $Val{'ARRAY(0x796b20)'}
zur|ck. Eine Stolperfalle besteht darin, da_ die
Perl-Dokumentation nichts aussagt |ber die Konsistenz dieser
Reprdsentation. Also testet die Funktion, ob der gleiche
Schl|ssel mvglicherweise schon f|r einen anderen Wert
existiert, und zdhlt notfalls hoch, z. B. $Val{'ARRAY(0x796b20)a'}
Eine Liste ist normalerweise ein Funktionsaufruf. Der
LISP-Ausdruck mu_ im Prinzip zum
Quelltext
umgeformt werden. Die Perl-Variablen f|r die Funktion listund
die Argumente apfel, birnewerden
gewonnen wie in "Symbolwerte" beschrieben. Im Quelltext
steht $f und $c anstelle $function
und $code, au_erdem finden Sie einen Test auf den
Datentyp "Funktion".
Ohne die Spezialform ist kein LISP lebensfdhig. Zu den
fundamentalen zdhlt die wenig beachtete QUOTE, die sich
hinter dem 'Hochkomma versteckt.
F|r die Spezialformen legen wir eine Assoziativliste %SpecialSource
an, deren Schl|ssel die Symbolnamen und deren Werte
Quelltextgeneratoren sind.
Unser Quelltextgenerator &GeneratePerlSource
mu_ so erweitert werden, da_ er bei einer Liste feststellt, ob
sie eine Spezialform bildet, und zwar mit .
Die QUOTE definieren wir so: . Tja, und
sofort brauchen wir einen weiteren Quelltextgenerator, der sich
von dem obigen nur dadurch unterscheidet, da_ er Symbole und
Listen "so nimmt, wie sie sind". Siehe special.pl und
eval.pl.
Die Spezialform set! |berschreibt eine Variable
dann, wenn diese existiert falls nein, Fehlermeldung.
Theoretisch kvnnten wir auch define als
Spezialform anlegen. Tun wir aber nicht, das gibtn Makro.
Zum Definieren verwenden wir den Namen **define**.
Zu den Spezialformen zdhlen u. a. IF, OR,
AND. Diese evaluieren ihre Ausdr|cke nachtrdglich
bei Bedarf (lazy evaluation). Beispiel:
Lisp> (or "so wahr ich hier stehe" was f|r'n Quatsch) ` "so wahr ich hier stehe"
OR hvrt auf zu rechnen, sobald es auf einen
wahren Ausdruck stv_t also einen, der nicht zu NIL
evaluiert. Dahinter darf jeder Quatsch der Welt stehen,
z. B. auch gerne mal ein Symbol, das gar nicht definiert
ist. Pfusch? Nein, sondern serivse Kurzschlu_logik.
LAMBDA ist in Scheme nichts weiter als eine
Spezialform, die eine Funktion erzeugt. Aus dem Ausdruck macht sie im Prinzip den Quelltext , plus Kontrolle auf
korrekte Argumentenzahl etc. Siehe lambda.pl.
Betrachten wir eine Primitive Funktion also eine, die
wir nicht in LISP realiseren kvnnen oder wollen
z. B. list:
$_LIST = &MakeFunction(\&MakeList)
(Die Funktion &MakeList gibts schon, die
gehvrt zu den Butter-und-Brot-Funktionen in unserem Lisp.)
K|rzer gehts kaum. Probe? Probe:
Lisp> (list 'apfel'birne) ` (apfel birne)
Eine weitere Vereinfachung bildet die Hilfsfunktion , die den Funktionsnamen nach Perl
|bersetzt, und die von sich aus wei_, da_ der Wert vom Typ
Funktion sein soll. Einige Funktionen, z. B.
Vergleichsfunktionen, definieren wir nicht einzeln, sondern am
St|ck in einer for-
Das Multiplizieren zweier rationaler Zahlen ist
trivial (Zdhler mal Zdhler durch Nenner mal Nenner und
k|rzen). Zum Addieren werden die Br|che gleichnamig
gemacht, die Zdhler addiert und die Summe anschlie_end
ebenfalls gek|rzt. Die Funktion addiert und
multipliziert Zdhler/Nenner-Paare unter Ber|cksichtigung der
Genauigkeit, das hei_t: wenn die eine Zahl ungenau ist, wird die
andere ungenau gemacht, und nur die Zdhler werden berechnet.
(Ausnahme: falls eine genaue 0 mit einer ungenauen
Zahl multipliziert wird, kommt eine genaue 0
heraus.)
Das Addieren zweier komplexer Zahlen geschieht durch
Addition der reellen und der imagindren Komponenten, die Subtraktion
durch Addition mit dem Negativwert. Das Multiplizieren
geht nach der Regel "jeder mit jedem". Die Funktion
addiert, multipliziert
Real/Imagindr-Gruppen.
Zum Dividieren multiplizieren wir mit dem Kehrwert.
Mit Herrn Binomi fdllt das gar nicht schwer: Um zur Zahl (a+b*i)
den Kehrwert 1/(a+b*i) zu finden, erweitern wir den
Bruch um (a-b*i), ergibt nach der 3. binomischen Regel (a-b*i)/(a2+b2)
oder a/(a2+b2)-b/(a2+b2)*i
k|rzen, und aus. Das erledigt f|r uns die Funktion .
Siehe number.pl.
Diese Funktion, abgek|rzt call/cc, dient dem
"geordneten R|ckzug" aus einem komplizierten Geflecht
aus Funktionsaufrufen. Sie verwendet eine lokale Variable namens $CallCCResult
und eine private LISP-Funktion mit dem Perl-Namen $exitfunc,
die einfach nur ihr Argument in $CallCCResult
schreibt und anschlie_end "stirbt".
Die call/cc erwartet als Argument einen
LAMBDA-Ausdruck, dem sie als Argument $exitfunc mit
auf die Reise gibt. Innerhalb des LAMBDA-Ausdrucks hat der
Verbraucher freie Bahn wenn er der Meinung ist, es sei zu
viel des Guten, ruft er die Ausstiegsfunktion auf, und diese
liefert dann das Resultat der Rechenoperation. Beispiel?
Beispiel:
Lisp> (**define** reelle_wurzel (lambda (x) (call/cc (lambda (abbruch) ; der "Abbruch" wird zur Ausstiegsfunktion. (if (< x 0) (abbruch 'geht-nicht!)) ; die Ausstiegsfunktion "Abbruch" verhindert den Rest. (sqrt x))))) ` REELLE_WURZEL
Lisp> (reelle_Wurzel 4) ` 2.0
Lisp> (reelle_Wurzel -4) ` GEHT-NICHT!
Wie ist es gemacht? Die Perl-Funktion eval sorgt
daf|r, da_ nicht der "Tod" von LISP eintritt, sondern
nur der Abbruch der aktuellen Funktion. Anschlie_end schaut call/cc
nach, ob der LAMBDA-Ausdruck etwas nennenswertes hervorgebracht
hat falls nein, hat die Ausstiegsfunktion zugeschlagen,
und das Resultat steht im $CallCCResult. Nur, wenn
da auch nichts drinsteht, gab es ein echtes Problem, das in der
Variablen steht. Der Code im Kern:
Die Beliebtheit von LISP steht nicht nur f|r seine
Mdchtigkeit, sondern auch f|r seine Einfachheit. Ist unser LISP
schon einfach? Na ja, es gdbe da noch einige
Verbesserungsmvglichkeiten, z. B. lokale Bindungen mit dem let-Konstrukt.
Das Makro erlaubt uns, aus einer relativ menschenfreundlichen
Syntax einen reguldren Ausdruck zu machen, z. B. aus den Ausdruck . Das Makro ist die Patentante des
Prdprozessors bei C.
F|r das Makro legen wir eine Assoziativliste %Macro
an, deren Schl|ssel die Makro-Namen und deren Werte
LISP-Funktionen enthalten. (Makros programmieren wir prinzipiell
in LISP.) Wir realisieren den Makro-Generator als Spezialform:
$SpecialSource{MACRO} = sub {
my $expr = shift;
my $sym = &CAR($expr);
my $MacroFunc = &CADR($expr);
"do { \$Macro{'"
. &GetSymbolName($sym)
. "'} = "
. &GeneratePerlSource($MacroFunc)
. ";"
. &GenerateQuotedSource($sym) # Makro-Symbol als R|ckgabewert
. " } " }
Und jetzt m|ssen wir &GeneratePerlSource so
erweitern, da_ er zufvrderst auf Makro testet und ggf. umformt:
while (&IsPair($expr) && $Macro{&GetSymbolName(&CAR($expr))}) { # Makro
my $MacroSym = &CAR($expr); # 'let
my $MacroName = &GetSymbolName($MacroSym); # "LET"
my $MacroFunc = $Macro{$MacroName}; # #<code ...>
my $PerlCode = &GetFunctionCode($MacroFunc); # code ...
$expr = &$PerlCode($expr) } # Expansion!
Nun kvnnen wir das Makro let definieren:
(macro let (lambda (bindings . body) (cons (cons 'lambda (cons (map car bindings) body)) (map cadr bindings)))) ` LET
Lisp> (let ((a 1) (b 2)) (+ a b)) ` 3
Na ja, war relativ einfach. Bei komplizierten Makros brauchen
wir aber zur Vorsorgeuntersuchung etwas, das uns die
Makro-Expansion einmal "trocken" vorf|hrt. Die
Funktion macrocode |bergibt den Code eines Makros.
Der Ausdruck z. B. |bergibt
den Code des Makros let. Damit kvnnen wir die
Umformung direkt beobachten, anstatt |ber Fehler zu rdtseln:
Lisp> ((macrocode 'let) '(let ((a 1) (b 2)) (+ a b))) ` ((LAMBDA (A B) (+ A B)) 1 2)
Die quasi-quote erlaubt die Evaluierung einzelner
Ausdr|cke innerhalb ihres Arguments mit den Schl|sselwvrtern unquote
und unquote-splicing (abgek|rzt: ,<expr>
und ,@<expr>). Das Makro define
erspart uns die ldstige LAMBDA-. Das Makro let*
erzeugt einen verschachtelten let-letrec kennen die
Symbole einander und sich selbst n|tzlich bei lokalen
Funktionen. Das Makro named-lambda macht Gebrauch
davon und erzeugt eine Funktion, bei der Selbstaufrufe auch nach
Umtaufe funktionieren. cond |berf|hrt eine
|bersichtliche Fallunterscheidung in einen verschachtelten if-Ausdruck,
case macht aus einer switch-cond-do und do*
erzeugen eine abweisende bedingte Programmschleife.
Der innere Ablauf bei der Makro-Umformung zeigt eine Einschrdnkung auf: Beim Kompilieren einer Funktion werden alle Makros vollstdndig angewendet. Also mu_ das Makro vorher bekannt sein. Sollten Sie eine Funktion definieren, die ein Makro verwendet, und das aufgerufene Makro erst spdter definieren, dann "denkt" der Compiler, es handele sich um eine normale Funktion ein Aufruf des Makros als Funktion f|hrt zum Fehler.
Das zwingt zur Erkenntnis: Das Makro wirkt innerhalb von LISP eigentlich als Stvrenfried. F|r die reine Funktionalitdt wdre es absolut verzichtbar. Seine einzige Daseinsberechtigung ist seine Benutzerfreundlichkeit.
Falls benutzerdefinierte Makros wirklich erforderlich sind, empfehle ich diese Vorgehensweise: Definieren Sie alle nvtigen Makros "am St|ck". Stellen Sie die Definitionen an den Anfang Ihres Programms. Packen Sie sie in eine eigene Datei. Vermeiden Sie anderweitige Verwendung der Makro-Symbole, auch im mit quote "entschdrften" Zustand. Andernfalls sehe ich lange und erf|llte Abende der Fehlersuche voraus.
Wenn dies LISP gestartet wird, lddt es zuerst die Datei Autoload.lsp sofern vorhanden. Hier kvnnen Sie Ihr LISP nach Gusto erweitern.
Der einzige grv_ere Flaschenhals in diesem LISP ist die Benutzereingabe, bei der der eingegebene Klartext zundchst in ein LISP-Datum umgewandelt wird, dieses wiederum in Perl-Quelltext, und dieser zu Code. In der Laufzeit LISP fast so schnell wie Perl.
Den zweitgrv_te Flaschenhals bildet <expr>),
bei dem das LISP-Datum |ber Klartext zu Code gemacht wird.
Eine weitere Bremse f|r den Durchsatz ist der Datentyp Liste, f|r den es in Perl keine unmittelbare Entsprechung gibt.
Abgesehen davon beschrdnkt sich der Mehraufwand auf die Typ-Kontrolle bei geschdtzten 95% aller Funktionen. In summa stellen wir fest: dies LISP ist immerhin fast so schnell wie Perl. Und das ist ja wohl recht ordentlich, gell?
Die Zahlenoperationen sind so genau, wie Perl eben zuld_t. Ziffernfolgen mit beliebiger Stellenzahl m|_ten ausdr|cklich programmiert werden, und das wdre bei den Bordmitteln von Perl vielleicht eine winzige Spur zu langsam
Unser LISP verzichtet auf einen Entlauser (siehe unten, Glossar: E). Im Fehlerfall "stirbt" das Programm und fdllt zur|ck auf die Eingabezeile. Seine Daten hat es nicht vergessen, allerdings den Absturzort. F|r den Notfall gibt es immer noch den Perl-Debugger, und f|r den Profi bleibt die Mvglichkeit, diesen LISP-spezifisch anzupassen.
Dies LISP optimiert Endrekursionen nicht zu Programmschleifen. Soll ein anderer machen. Das Werkzeug ist da (siehe Makros).
Es gibt keinen Support, ich habe auch noch Familie. Betrachten Sie dies LISP als Hack vom Hacker f|r Hacker. Fehlerr|ckmeldungen sind nat|rlich hochwillkommen (bitte Email an Wolf-Dieter.Busch@T-Online.de)!
LISP ist eine einfache und zugleich mdchtige Programmiersprache, aber keine schnelle. Ein vern|nftiger Mensch w|rde z. B. niemals Bitmap-Manipulationen damit realisieren. Das Heimstadion von LISP ist die Logik. Die professionelle Applikation Schematext z. B. ist in LISP programmiert. Sie verwaltet gro_e Bestdnde von Internet-Dokumenten mitsamt einem komplizierten Geflecht von Hyperlinks (siehe Glossar: H). F|r alles, was |ber die Logik hinaus geht, ruft diese Applikation Fremdprogramme auf, z. B. Help-Compiler von Drittherstellern. Andere Anwendungsbereiche sind z. B. maschinelle Roh-\bersetzungen in Fremdsprachen jedenfalls, solange es noch keine vollstdndige Abbildung menschlicher Sprache in ein formales Modell gibt. (Wenn ich schon nicht alles Gesprochene verstehe aber lassen wir Politik und Betriebsrat au_en vor.)
Wenn ein Perl-Programm vor komplizierten logischen oder logistischen Problemen steht, kann es unser LISP einbinden. Die Funktion &CommandLisp( ) bildet eine pflegeleichte Klartext-Schnittstelle. Siehe unten: Technik.
Perl erlaubt es, den vollstdndigen Quelltext dieses LISP innerhalb eines Batchfiles auszuf|hren. (Vor ein paar Jahren hdtten wir |ber die Vorstellung gelacht.) Das ermvglicht praktische LISP-\bungen in Schule, Studium und Wohnzimmer. F|r den Beruf empfehle ich es nur bedingt, denn es gibt keinen Support siehe oben.
Dies LISP ist in einer Hochsprache geschrieben, die syntaktische Dhnlichkeiten mit C hat. Vielleicht findet sich ja ein Freiwilliger, der mit einem geschickten Prdprozessor den Perl-Quelltext umformt in einen C-Quelltext? Wenn ich ein vervollstdndigtes Update nachschiebe? (Man wird ja noch fragen d|rfen )
In der ersten Fassung enthielt dies LISP genau den Funktionsumfang, den Scheme als wesentlich ansieht. Aber naheliegenderweise sollte es nicht weniger kvnnen als Perl.
Der Datentyp hash erlaubt schnelleren Zugriff auf
Schl|ssel-.
Die Funktion erzeugt
eine Assoziativliste. Mit setzen Sie ein neues Schl|ssel- rufen Sie es ab. Falls der Schl|ssel
nicht vorkommt, gibt diese Funktion nil zur|ck. Mit
der Funktion erhalten Sie eine
\bersicht der Schl|ssel, mit
eine \bersicht der Werte.
Falls Sie mit Eigenschaften (properties) arbeiten wollen,
bietet sich anstelle der klassischen assoc-Listen
die wesentlich schnellere Assoziativlisten des Typs hash an. Der
Schl|ssel mu_ aber ein Wort sein. Die neue Funktion object->string
erzeugt aus einem beliebigen Lisp-Objekt dessen
Bildschirm-Reprdsentation als Wort.
Der Mustervergleich ist primitiv programmiert: greift das
Muster /C/ aus dem Wort "abcdef"
ab wegen des "i" nimmt es keine
R|cksicht auf Gro_-. Wenn es keinen Treffer
erzielt, |bergibt es nil.
In Abweichung von der Reinen Lehre ist jetzt die Lokale
Sichtweise von Variablen eingebaut, und zwar mit dem Makro , das syntaktisch analog let
funktioniert, und das eine neue Spezialform lambda-local
aufruft.
Die Eingabe von Zeichenketten darf jetzt mehrzeilig
ausfallen. Au_erdem ist ein Fehler behoben: der Parser hat in
der ersten Fassung die Sonderzeichen \" etc.
nicht zuverldssig verarbeitet.
Die Funktion read-line
liest eine Zeile von Tastatur oder Port und |bergibt den Inhalt
als Wort. Merkw|rdigerweise nicht im Revised4 Report
erwdhnt; mvglicherweise (?) deshalb, weil mit read-char
programmierbar.
Aufgrund einer versehentlich gelvschten Programmzeile fehlte
in der ersten Fassung die Funktion char?.
Der Fehler ist behoben.
Die Symbole sind nun in einer Tabelle zusammengefa_t.
Gleichnamige Symbole kommen nur einmal vor. Damit kann die
Funktion eq?
"ehrlich" werden und auch bei Symbolen auf wirkliche
Identitdt testen anstatt wie bisher auf Typ- und
Wert-Gleichheit. Erhebliche Speicherersparnis und damit
reduzierte Speicherbereinigung gibts vermutlich nur, wenn man
intensiv mit Eigenschaften (properties) arbeitet. (Ich hab da ein
Projekt am Laufen mit deutscher Grammatik.)
Bei Eingabe eines einzelnen Punkts in der REP meldete dies
LISP bisher single dot is
ambigous. Das ist falsch. Richtig ist: ambiguous. Der
orthographische Fehler ist behoben. (Hoffentlich hat er niemandem
Kopfzerbrechen bereitet.)
Wenn beim Lesen einer Datei mit read
oder load ein
Fehler mit Abbruch auftritt, wird die Datei nicht geschlossen.
Wenn beim Schreiben in eine Datei ein Fehler mit Abbruch auftritt, wird die Datei nicht geschlossen.
Beim Verschachteln der Funktion call-with-current-continuation
(call/cc) kann
aus dem inneren call/cc-
Die Funktion map
verarbeitet nur eine Liste als 2. Argument, nicht mehrere.
Dies LISP enthdlt die Funktionen, die im Revised Report als essential gekennzeichnet sind. F|r die normale LISP-Syntax lesen Sie den Revised Report oder irgend ein nicht-produktspezifisches Lehrbuch zu Scheme. Hier finden Sie, was dar|ber hinausgeht, was also dort nicht vorkommen kann.
Speicherverwaltung der Revised Report trifft genauere Aussagen zur Symbolbindung. Unter Perl gibt es keine Pointer, sondern Referenzen. Sollte den Anwender nicht weiter ber|hren.
Dies LISP ist geeignet f|r den Aufruf aus reguldren
Perl-Programmen heraus, sozusagen im Serverbetrieb. Als
Schnittstelle dient Funktion &CommandLisp(
). Sie
erhdlt als Argument einen Klartext, z. B. , berechnet ihn und
|bergibt das Resultat wiederum als Klartext, in unserem Beispiel
. Die einzige erforderliche Dnderung im
Quelltext besteht darin, den Aufruf der read-eval-print-Schleife
am Ende des Programms auszukommentieren. Wie gesagt:
pflegeleicht.
erlaubt als optionales drittes
Argument eine Vergleichsfunktion, z. B. `
. Ursache: Faulheit des Autors, damit
er assv und assq leichter
programmieren konnte. Empfehlung: Ignorieren.
Spezialform zum Definieren
einer neuen Symbolbindung. Wird vom Makro define verwendet.
Funktion, f|hrt zu Abbruch
des Programms (Ausstieg zur Eingabezeile). Zweck: Fehlerkontrolle
zur Entwicklungs- und Laufzeit.
funktionsgleich mit .
Zweck: Unterst|tzt das Verstdndnis von LISP im Schulgebrauch;
kommt dem dsthetischen Empfinden des Autors entgegen.
Empfehlung: Schlie_en Sie sich mir an!
erlaubt als optionales drittes
Argument eine Vergleichsfunktion, z. B. ` .
Ursache: Faulheit des Autors, damit er memv und memq leichter
programmieren konnte. Empfehlung: Ignorieren.
`
string)
Funktion, berechnet einen Perl-Ausdruck und |bergibt das
Resultat als Liste von Wvrtern. Erwartet als Argument ein Wort.
Zweck: Schnittstelle nach "drau_en", z. B. zur
Nutzung der Spezialfdhigkeiten von Perl, oder f|r
Implementierung einer Funktion system, die den Kommandoprozessor
lddt. Empfehlung: Klasse, um das System abzuschie_en!
Funktion, |bergibt den Typ des
Datums expr als Symbol. Zweck: Entlausen. Empfehlung: Entfernen
Sie nach Fertigstellung des Programms alle type-of-Kontrollen.
Beispiele (das Symbol ist gebunden an das Wort
):
(type-of 'apfel) ` SYMBOL (type-of apfel) ` STRING
Spezialform f|r
Programmschleifen. Wird von den Makros und
verwendet.
Die Spezialform macro ist ziemlich locker angelehnt an die unverbindlichen Empfehlungen des Revised Report. Beispiel:
(macro let* (lambda (expr) (define bindings (cadr expr)) (define body (cddr expr)) (if (null? (cdr bindings)) `(let ,bindings ,@body) `(let (,(car bindings)) (let* ,(cdr bindings) ,@body))))) ` LET*
Die Funktion |bergibt den Code eines
Makros. Zweck: Entlausen. Beispiele:
(macrocode 'let*) ` #<Code(0x8718c4)>
((macrocode 'let*) '(let* ((a 'wurst) (b "Kdse")) (list a b))) ` (LET ((A (QUOTE WURST))) (LET* ((B "Kdse")) (LIST A B)))
Die Funktion lvst alle Makros
innerhalb eines Ausdrucks auf. Beispiel: `
Um eine \bersicht der Makros abzurufen, empfehle ich diese Definition:
(define (macros) (map string->symbol (perl "keys(%Macro)")))
Die Funktionen, die nicht als essential gekennzeichnet sind,
sind nicht vollstdndig implementiert. Die Funktion
ist nicht implementiert.
, , , ,
, , ,
, , ,
, , , ,
, , , ,
, ,
C:\WINDOWS\DESKTOP perl -v
This is perl, version 5.004_02 Copyright 1987-1997, Larry Wall
GNU General Public License, which may be found in the Perl 5.0 source kit. Gurusamy Sarathy (Just Another Perl Porter) gsar@umich.edu 08-AUG-1997
http://www.perl.com/CPAN/ports/win32/Gurusamy_Sarathy/
A B C D E F G H I L M N O P Q R S T V W Z
die vier Grundrechenarten. Beispiel: `
arithmetische
Vergleichsfunktionen. Beispiel:
`
A |
ermittelt den Absolutwert einer Zahl.
Beispiel: `
ermittelt den Winkel einer
komplexen Zahl in geometrischer Darstellung. Beispiel: `
hdngt mehrere Listen zusammen. Beispiel: `
wendet eine Funktion auf eine Liste an.
Beispiel: `
assoc, assq, assv
durchsucht eine Liste von Listen nach einem bestimmten Element. assocverwendet
als Vergleichsfunktion equal?, assv
verwendet eqv?, assq verwendet eq?.
Beispiel: `
B |
boolean? stellt fest, ob sein Argument ein
Wahrheitswert ist. Beispiel: `
C |
car ermittelt das erste Element einer
Liste. Komplement zu . Beispiel: ` a
cdr ermittelt den Rest einer Liste ohne
das erste Element. Komplement zu . Beispiel: `
caar, cadr,
cdar, cddr, caadr,
cddddr
Kombinationen aus car und cdr
insgesamt 28 Kombinationen. cadr z. B.
ist so definierbar: .
Beispiel: `
b
ceiling ermittelt die ndchsthvhere Ganzzahl.
Beispiel: `
6
char? stellt fest, ob ein Objekt ein Buchstabe
ist. Beispiel: `
#t
char->integer findet den ASCII- bzw. ANSI-Wert
eines Buchstaben. Hinweis: Verwendet die Perl-Funktion ord(). Beispiel: ` 65
char-alphabetic? stellt fest, ob ein Buchstabe
alphabetisch ist. Beispiel: ` ()
char<=?, char<?, char=?,
char>=?, char>? vergleicht
Buchstaben. Beispiel: ` ()
char-ci<=?, char-ci<?, char-ci=?,
char-ci>=?, char-ci>? vergleicht
Buchstaben ohne R|cksicht auf Gro_-Kleinschreibung. Beispiel: ` #t
char-downcase findet den passenden
Kleinbuchstaben. Beispiel: ` #\a
char-lower-case? stellt fest, ob ein Buchstabe
kleingeschrieben ist. Beispiel:
` ()
char-numeric? stellt fest, ob eine Buchstabe eine
Ziffer darstellt. Beispiel: ` #t
char-upcase findet den passenden Gro_buchstaben.
Beispiel: `
#\A
char-upper-case? stellt fest, ob ein Buchstabe
gro_geschrieben ist. Beispiel:
` #t
char-whitespace? stellt fest, ob ein Buchstabe
ein Leerzeichen ist. Beispiel:
` #t
close-input-port schlie_t eine gevffnete
Eingabe-Datei. Beispiel: ` #t. Seiteneffekt: der
Eingabeport port wird geschlossen.
close-output-port schlie_t eine
gevffnete Ausgabedatei. Beispiel:
` #t Seiteneffekt:
der Ausgabeport port wird geschlossen.
complex? stellt fest, ob ein Objekt eine komplexe
Zahl ist. Beispiel: ` )
cons erzeugt einen Knoten. Beispiel: `
current-input-port |bergibt das Filehandle, das
f|r die Tastatureingabe zustdndig ist. Beispiel: (current-input-port)
` #<Port>
current-output-port |bergibt das Filehandle, das
f|r die Bildschirmausgabe zustdndig ist. Beispiel: (current-output-port)
` #<Port>
D |
display schreibt ein Objekt auf Bildschirm oder
Port. Wvrter erscheinen ohne Gdnsef|_chen, Buchstaben ohne
Schrdgstrich und Raute. Beispiel: ` #t.
Seiteneffekt: schreibt auf den Bildschirm.
E |
eof-object? stellt fest, ob ein Objekt ein
Dateiende-Objekt ist. Beispiel:
` ()
eq?, eqv?, equal?
vergleicht Objekte. eq? Vergleicht auf Identitdt,
wobei gleichnamige Symbole als identisch gelten. eqv?
vergleicht auf gleichen Typ und gleichen Wert (n|tzlich bei
Konstanten), equal? vergleicht Listen rekursiv.
Beispiel: ` #t
even? stellt fest, ob eine Zahl gerade ist.
Beispiel: `
()
exact? stellt fest, ob eine Zahl genau ist.
Beispiel: `
#t
exact->inexact wandelt eine genaue Zahl um in
eine ungenaue. Beispiel: ` 4.0
exit verld_t LISP und Perl. Beispiel: (exit)
` Seiteneffekt: Ende des
Programms.
exp berechnet den Exponenten zur
Eulerschen Zahl e. Beispiel. ` 2.718281828459
F |
floor ermittelt die ndchstkleinere Ganzzahl.
Beispiel: `
5
for-each wendet eine Funktion auf eine Liste von
Argumenten an. Funktionsgleich map. Beispiel: `
G |
gcd ermittelt den ggT (grv_ten
gemeinsamen Teiler, greatest common divisor). Beispiel: ` 3
H |
hash-keys liefert eine \bersicht der Schl|ssel
einer Assoziativliste. Beispiel:
` ("Gem|t")
hash-ref ermittelt den Wert eines Schl|ssels aus
einer Assoziativliste. Beispiel: `
hash-set! setzt ein Schl|ssel-Wert-Paar in einer
Assoziativ-Liste. Beispiel: ` .
Seiteneffekt: der Schl|ssel "Gem|t" in
der Assoziativliste ZUSTAND wurde gedndert.
hash-values liefert eine \bersicht der Werte
einer Assoziativliste. Beispiel:
`
I |
inexact? stellt fest, ob eine Zahl ungenau ist.
Beispiel: `
()
imag-part ermittelt den imagindren Anteil einer
komplexen Zahl. Beispiel: ` 2
inexact->exact wandelt eine ungenaue Zahl um
in eine genaue. Beispiel: ` 4
input-port? stellt fest, ob ein Objekt das
Filehandle einer Eingabedatei ist. Beispiel: ` #t
integer->char ermittelt einen Buchstaben nach
ASCII bzw. ANSI. Verwendet die Perl-Funktion chr().
Beispiel: ` #\A
integer? stellt fest, ob eine Zahl ganzzahlig
ist. Beispiel: `
()
L |
lcm ermittelt das kgV (kleinste gemeinsame
Vielfache, least common multiple). Beispiel:
` 30
length berechnet die Elementzahl einer Liste.
Beispiel: `
2
list bildet eine Liste. Beispiel: `
list->string bildet aus einer Buchstaben-Liste
ein Wort. Beispiel:
` "Wolf"
list->vector erzeugt einen Vektor aus den
Elementen einer Liste. Beispiel:
`
list-ref ermittelt ein Listenelement aus seiner
Position in der Liste. Beispiel:
` b
list? stellt fest, ob ein Objekt eine ordentliche
Liste ist. Beispiel: ` ()
load lddt einen Quelltext. Beispiel: `
#t. Seiteneffekt: der Quelltext autoload.lsp
wird ausgef|hrt.
log ermittelt den nat|rlichen Logarithmus (also
zur Basis der Eulerschen Zahl e). Beispiel: ` 0
M |
magnitude ermittelt den Absolutwert einer
Komplexen Zahl. Beispiel: ` 5.0
make-hash erzeugt eine Assoziativliste. Beispiel:
`
make-polar erzeugt eine Komplexe Zahl mit dem
ersten Argument als Absolutwert und dem zweiten als Winkel.
Beispiel: `
-2.0807341827357+4.5464871341284i
make-rectangular erzeugt eine Komplexe Zahl mit
dem ersten Argument als reellem und dem zweiten als imagindrem
Anteil. Beispiel: ` 5+2i
make-string erzeugt ein neues Wort, wahlweise mit
einem Vorgabewert. Beispiel: ` "xxxxx"
map wendet eine Funktion auf die Elemente einer
Liste an. Funktionsgleich . Beispiel: `
match pr|ft ein Wort auf das Vorkommen eines Musters.
Beispiele: ` ; `
()
max ermittelt den grv_ten Zahlenwert. Beispiel:
` 5
member, memq, memv
stellt fest, ob ein Element in einer Liste vorkommt. member
vergleicht mit equal?, memq vergleicht
mit eq?, memv vergleicht mit eqv?.
Beispiel: `
min ermittelt den kleinsten Zahlenwert. Beispiel:
` 3
modulo berechnet den Rest bei Division. Resultat
hat Vorzeichen des Nenners. Beispiel: ` 2
N |
negative? stellt fest, ob eine Zahl negativ ist.
Beispiel: `
#t
newline schreibt einen Zeilenwechsel ins Display
oder einen Port. Beispiel: (newline) ` #t. Seiteneffekt:
Zeilenwechsel.
not negiert einen Wahrheitswert. Funktionsgleich null?.
Beispiel: `
()
null? stellt fest, ob ein Objekt die Leere Liste
ist. Funktionsgleich not. Beispiel:
` #t
number->string macht aus einer Zahl ein Wort.
Beispiel: ` "435.2"
number? stellt fest, ob ein Objekt eine Zahl ist.
Beispiel: `
()
O |
object->string |bergibt die
Bildschirm-Reprdsentation eines Objekts. Wenn ein drittes
Argument gegeben ist, erscheinen Worte ohne Gdnsef|_chen und
Buchstaben ohne Raute und Schrdgstrich. Beispiele: `
; `
odd? stellt fest, ob eine Zahl ungerade ist.
Beispiel: ` #t
open-input-file vffnet eine Eingabedatei,
|bergibt das Filehandle. Beispiel: ` x. Seiteneffekt: die
Datei Eingabedatei test.txt wird gevffnet.
open-output-file vffnet eine
Ausgabedatei, |bergibt das Filehandle. Beispiel: ` y. Seiteneffekt: die
Datei Ausgabedatei test.out wird gevffnet.
output-port? stellt fest, ob ein Objekt das
Filehandle einer Ausgabedatei ist. Beispiel: (output-port? p ` ) #\t
P |
pair? stellt fest, ob ein Objekt ein Knoten ist.
Beispiel: `
#t
nicht unterst|tzt
positive? stellt fest, ob eine Zahl positiv ist.
Beispiel: `
#t
procedure? stellt fest, ob ein Objekt eine
Funktion ist. Beispiel: ` #t
Q |
quotient dividiert zwei Zahlen ganzzahlig.
Beispiel: `
4
R |
rational? stellt fest, ob eine Zahl rational ist.
Funktionsgleich number?. Beispiel: ` #t
read liest einen Ausdruck von Tastatur oder Port.
Beispiel: (read) (Benutzereingabe: apple)
` apple
read-char liest einen Buchstaben von Tastatur
oder Port. Beispiel: (read-char) (Benutzereingabe: a)
` #\a
read-line liest eine Zeile von Tastatur oder Port
und |bergibt ihn als Wort. Beispiel: (read-line)(Benutzereingabe:
ein, ich bin
ein Arbeitsbdr) `
"
real? stellt fest, ob eine Zahl reell ist.
Funktionsgleich number?. Beispiel:
` #t
real-part ermittelt den reellen Anteil einer
komplexen Zahl. Beispiel: ` 5
remainder berechnet den Rest bei Division.
Resultat hat Vorzeichen des Dividenden. Beispiel: ` 2
reverse erstellt eine Liste mit umgekehrter
Reihenfolge. Beispiel: `
round rundet eine Zahl kaufmdnnisch. Beispiel: ` 4
S |
set-car! ersetzt den CAR einer Liste durch ein
anderes Element. Destruktiv. Beispiel:
` apfel.
Seiteneffekt: das erste Element der Liste x ist mit
dem Symbol apfel |berschrieben.
set-cdr! ersetzt den CDR einer Liste durch ein
anderes Element. Destruktiv. Beispiel: ` (birne).
Seiteneffekt: der Rest der Liste x (ohne erstes
Element) ist mit der Liste birne |berschrieben. (Es
d|rfte jetzt den Wert haben.)
string bildet aus Buchstaben ein Wort. Beispiel: ` "Wolf"
string->list bildet aus einem Wort eine Liste
von Buchstaben. Beispiel: `
string->number bildet aus einem Wort eine
Zahl. Beispiel:
` 45.2
string->symbol erzeugt ein aus einem Wort
Symbol. Erlaubt auch Sonderzeichen. Beispiel: ` Wolf
string-append hdngt Wvrter zusammen. Beispiel: ` "abc"
string<=?, string<?, string=?,
string>=?, string>? vergleicht
Wvrter. Beispiel: ` ()
string-ci<=?, string-ci<?, string-ci=?,
string-ci>=?, string-ci>?
vergleicht Wvrter ohne R|cksicht auf Gro_- und
Kleinschreibung. Beispiel: ` #t
string-length berechnet die Buchstabenzahl eines
Worts. Beispiel: ` 3
string-ref ermittelt einen Buchstaben aus seiner
Position im Wort. Beispiel:
` #\c
string-set! |berschreibt einen Buchstaben in
einem Wort. Destruktiv. Beispiel: ` "axc".
Seiteneffekt: das Wort abc wird kvrperlich
|berschrieben mit axc.
string? Stellt fest, ob ein Objekt ein Wort ist.
Beispiel: ` #t
substring findet einen Teil eines Worts.
Beispiel: `
symbol->string ermittelt den Namen eines
Symbols. Beispiel: ` "APPLE"
symbol? stellt fest, ob ein Objekt ein Symbol
ist. Beispiel: ` #t
T |
truncate schneidet den Nachkommateil einer Zahl
ab. Beispiel: `
4
V |
vector erzeugt einen neuen Vektor aus seinen
Argumenten. Beispiel: `
vector->list macht aus einem Vektor eine
Liste. Beispiel: `
vector-length ermittelt die Zahl der Elemente
eines Feldes. Beispiel: ` 3
vector-ref ermittelt ein Element eines Feldes aus
seiner Position. Beispiel: ` b
vector-set! |berschreibt ein Element eines
Feldes. Destruktiv. Beispiel:
` apple.
Seiteneffekt: das erste Element des Feldes v ist nun das Symbol
apple.
vector? stellt fest, ob ein Objekt ein Vektor
ist. Beispiel: ` #t
W |
write schreibt einen Ausdruck auf Bildschirm oder
Port. Wvrter erscheinen in Gdnsef|_chen, z. B. "Wort",
Buchstaben mit Raute und Schrdgstrich, z. B. #\b.
Kein Zeilenwechsel. Beispiel: ` #t.
Seiteneffekt: auf dem Bildschirm steht "abc".
write-char schreibt einen Buchstaben ohne Raute
und Schrdgstrich auf Bildschirm oder Port. Beispiel: ` #t.
Seiteneffekt: auf dem Bildschirm steht a.
Z |
zero? stellt fest, ob eine Zahl gleich Null ist.
Beispiel: `
#t
Debugging Neudeutsch: Suche nach Denkfehlern im Programm, mit Abstand die arbeitsintensivste Phase beim Programmieren. Fdllt bei LISP k|rzer aus als bei Perl, entfdllt aber nie ganz.
Entlausen StarckDeutsch: Debugging (siehe oben).
Hyperlink Neudeutsch: Vollelektronischer Ersatz f|r Wollfdden beim Buch in Papierform. Vorteile: geht schneller und bequemer; Nachteile: versagt bei Wollfdden in der Maus, also sofort in den Papierkorb damit!
Zur|ck zu Wolf Busch's Heimatseite