Das Wort
Lisp bedeutet
List Processing, und diese
Abkürzung beschreibt genau den Zweck der Sprache Lisp -
Listenbearbeitung. Fast alles, womit man in Lisp zu tun hat, sind
Listen. Es gibt keine Beschränkungen, was, wie und wieviel man
in Listen packen kann. Insbesondere kann man - das war ja zu
erwarten - auch Listen in Listen unterbringen. Ein wesentlicher
Unterschied zwischen Lisp und fast allen anderen Programmiersprachen
ist, daß auch die Programme ganz normale Listen sind. Da gibt es
neuerdings jedoch Einschränkungen, auf die wir gleich eingehen
werden.
Es gibt keinen formalen Unterschied zwischen Programmlisten
einerseits und Datenlisten andererseits. Es gibt Lisp-Dialekte, wo
zwischen Programm- und Datenlisten noch ein geringer Unterschied
besteht. AutoLISP ist hier, zumindest in den Versionen bis 14,
konsequent: Es gibt gar keinen Unterschied. Man kann also mit
(setq) und den in diesem Kapitel noch zu besprechenden
Listen-Bearbeitungsfunktionen nicht nur Datenlisten, sondern auch
Funktionen bzw. Programme erzeugen.
Wenn ein Programm in einer Sprache wie C, Pascal oder Basic fertig
geschrieben und kompiliert ist, ist es eben fertig und kann nicht
mehr geändert werden, es sei denn, man ändert den Quellcode und
kompiliert es neu. In Lisp ist das völlig anders. Erstens hat der
Benutzer Zugriff auf den Programmcode, zweitens kann man Programme
schreiben, die Zugriff auf ihren eigenen Programmcode haben und
sich daher selbst während der Benutzung verändern können. Drittens
besteht die Möglichkeit, Programme zu schreiben, die durch Benutzung
dazulernen und immer weiter wachsen.
Wie bereits gesagt, verhält sich der seit AutoCAD 2000 verwendete
Lisp-Interpreter nicht mehr ganz so. Durch den Benutzer definierte
Funktionen werden im Speicher nicht mehr als Listen abgelegt,
sondern haben einen eigenen Datentyp. Sobald die Funktion definiert
ist, kommt man im Arbeitsspeicher nicht mehr an die den Funktionscode
enthaltenden Listen heran. Zum Glück gibt es aber Mittel und Wege,
im Bedarfsfall auch Funktionen zu erzeugen, die als Listen gespeichert
werden. Mehr dazu aber in den entsprechenden Kapiteln.
Doch nun zu den Listen und ihrer Bearbeitung: Wir haben bereits in
den ersten beiden Kapiteln mit Listen gearbeitet, ohne es zu bemerken,
denn im Prinzip ist alles, was von zwei runden Klammern umgeben ist,
schon eine Liste. Sogar ein Klammerpaar, in dem gar nichts steht,
ist eine Liste. Die leere Liste ist übrigens identisch mit
nil,
das gleich besprochen wird, und damit haben wir nun einen wunderbaren
Anknüpfungspunkt ans Ende des letzten Kapitels. Wenn wir also eingeben:
(setq var1 3.5) => 3.5
(setq var1 ()) => nil
dann haben wir zuerst einen Wert an das Symbol
var1 gebunden
und anschliessend den Wert wieder entfernt (durch Zuweisung von nil).
Die Schreibweisen
nil,
() und
'() sind äquivalent,
alle drei Ausdrücke evaluieren zu
nil. Die dritte Schreibweise muß
dabei näher erläutert werden: Das Quote-Zeichen (Hochkomma) verhindert
natürlich die Evaluation. Da aber der Inhalt sowieso
nil ist
und nil immer zu nil evaluiert, entsteht kein Unterschied.
Wo wurde denn nun schon mit Listen gearbeitet? Wenn die Definition
stimmt, daß alles zwischen zwei Klammern eine Liste ist, dann sind
die beiden Ausdrücke des letzten Beispiels Listen. Der zweite Ausdruck
ist sogar eine verschachtelte Liste. Betrachten wir nun einen
Lisp-Ausdruck, der in dieser oder ähnlicher Form oft vorkommt:
(setq farben '("rot" "grün" "blau"))
Lassen Sie uns diesen Ausdruck analysieren:
(setq) weist
dem Symbol den Wert
("rot" "grün" "blau") zu. Das Symbol
farben wird nicht evaluiert,
(setq) verhindert das.
Die Liste muß gequotet werden - warum? Würden wir die Liste nicht
quoten, würde nach der Regel Innen-nach-Außen zunächst versucht,
die Funktion "rot" mit den Argumenten "grün" und "blau" aufzurufen,
da ja bei
(setq) das zweite Argument stets evaluiert wird.
Eine solche Funktion existiert aber nicht, und sie kann auch gar
nicht existieren.
Der Grund dafür ist einfach: Ein Funktionsname (der ja ein Symbol
ist) darf keine Anführungszeichen enthalten. "rot" kann also kein
Funktionsname sein, und es kann genausowenig überhaupt ein Symbol
sein. Mit anderen Worten: An "rot" kann man niemals einen Wert
binden - "rot" kann nur ein Wert sein, der an etwas gebunden werden
kann.
An dieser Stelle muss aber, bevor es weiter geht, noch ein wenig
mehr geklärt werden. Im letzten Beispiel haben wir es nicht mehr
mit Zahlen als Werten zu tun, sondern mit Zeichenketten, die man
auf Neudeutsch auch Strings nennt. Zeichenketten werden immer in
Anführungszeichen gesetzt, und genau wie Zahlen sind sie nicht
weiter evaluierbar, d.h. sie evaluieren immer zu sich selbst.
Symbole, wie
farbe im letzten Beispiel, sind etwas völlig
anderes: Sie werden nicht in Anführungszeichen gesetzt, sind
aber evaluierbar. Symbole sind schlicht und einfach Namen - für
Variablen, Funktionen, Listen und alles Mögliche, was es in Lisp
so gibt. Sie werden auch oft benutzt, ohne dass ihnen überhaupt
ein Wert zugewiesen wird.
Bisher wurde auch das Wort 'Variable' hier benutzt, ohne dass es
erklärt wurde. Als Variable bezeichnet man (auch in anderen
Programmiersprachen) den Sachverhalt, dass unter einem festgelegten
Namen wechselnde Werte abgelegt werden. Letztendlich sind aber
Variablen nur ein gedankliches Konzept - für Lisp handelt es sich
dabei immer nur um die wechselnde Bindung von Werten an Symbole.
Das Gegenteil von Variablen sind Konstanten - in Lisp gibt es
genau zwei davon, eine davon ist
pi.
Wenn wir das Gesagte noch einmal gründlich überdenken, kommen wir
zu folgendem Schluß: Wenn in Lisp eine Liste evaluiert werden
soll, dann muß sie als erstes Element einen Funktionsnamen und
danach eine für die Funktion gültige Anzahl von passenden Argumenten
enthalten, sonst kann aus der Evaluation nur eine Fehlermeldung
entstehen. Wir müssen also beim Erzeugen von Listen immer darauf
achten, die Evaluation zu verhindern.
Wenn wir Listen auf direktem Eingabeweg an Symbole binden, tun
wir dies, indem die Listeneingabe gequotet wird:
(setq das-erste-dutzend
'(1 2 3 4 5 6 7 8 9 10 11 12)
)
(setq ein-paar-klamotten
'("Hemd" "Hose" "Jacke" "Rock")
)
(setq bekannte-funktionen
'(+ - * / float fix rem exp expt set setq)
)
Wir sollten uns jetzt ein paar Gedanken machen, welche Arten
von Werten wir in diese Listen gepackt haben. Das erste Dutzend
enthält offensichtlich nur Integerzahlen. Die Klamotten-Liste
enthält Zeichenketten, und die Liste mit allen bisher gelernten
Funktionen enthält Symbole, also Namen, an die entweder schon
Werte gebunden sind, oder zumindest gebunden werden können.
Höhere Ansprüche an den Programmierer stellen Listen, in denen
alle möglichen Datentypen durcheinander vorkommen:
(setq lispziger-allerlei
'( 1 "Hemd" float 16.34 pi () (expt 10 3))
)
Diese Liste ist zwar nicht schwieriger zu erzeugen als jede
andere, die Weiterverarbeitung kann allerdings leichte Probleme
aufwerfen. Zulässig und formal korrekt ist sie aber auf jeden
Fall.
Stellen Sie sich vor, es hätte gerade das Telefon geklingelt.
Sie kommen zurück und haben dummerweise vergessen, was in
lispziger-allerlei so alles enthalten ist. Was nun? Sie
könnten jetzt auf den Gedanken kommen, einfach mal bei AutoCAD
anzufragen. Das wäre ja an sich nicht falsch, aber AutoCAD
antwortet auf Ihre Eingabe
(lispziger-allerlei) immer
nur mit 'Keine Funktion'.
Natürlich - jetzt fällt es uns wieder ein: Die geklammerte
Eingabe führt zum Funktionsaufruf, aber diese Liste enthält
gar keinen Funktionsaufruf. Wenn Sie einfach den Wert sehen
möchten, der einem Symbol zugeordnet ist, müssen Sie bei der
AutoCAD-Eingabe ein Ausrufungszeichen davorsetzen:
!lispziger-allerlei
=> ( 1 "Hemd" FLOAT 16.34 PI () (EXPT 10 3))
Wir müssen also das bisher Gesagte etwas relativieren: AutoCAD
leitet jede Eingabe, die mit einer linken runden Klammer oder
einem Ausrufungszeichen beginnt, an AutoLISP weiter. Das war's
dann aber auch, andere Möglichkeiten gibt es nicht. Was ist
jetzt aber der Unterschied zwischen einer geklammerten Eingabe
und einer, die mit
! beginnt?
Es ist ganz einfach: Es wird jede Eingabe evaluiert. Wenn Sie
eine Liste evaluieren möchten - besser gesagt: wenn Sie eine
Liste evaluieren lassen möchten - dann sollten Sie den Ausdruck
klammern. Es muß also ein gültiger Funktionsaufruf zwischen den
Klammern stehen. Wollen Sie hingegen ein einzelnes Symbol
evaluieren (lassen), dann verwenden Sie das Ausrufungszeichen.
In unserem Beispiel wird das Symbol
lispziger-allerlei
zum damit verbundenen Inhalt evaluiert.
Übungsaufgaben
-
Sie haben folgendes eingegeben:
(setq var1 27) => 27
(float var1) => 27.0
Wenn Sie nun (setq var2 var1) eingeben, welchen
Wert hat dann die Variable var2?
-