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 list
und
die Argumente apfel
, birne
werden
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. assoc
verwendet
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