Was kann man nun mit Listen anfangen? Nun - niemand will Ihnen
den Mut nehmen, aber mit Ihrem jetzigen Wissensstand herzlich
wenig. Lassen Sie uns daher einige Möglichkeit der Bearbeitung
von Listen besprechen. Sie werden wahrscheinlich nicht auf
Anhieb wissen, wieviele Elemente die Liste
lispziger-allerlei
aus dem letzten Kapitel enthält. Also nachzählen - oder:
(setq lispziger-allerlei
'( 1 "Hemd" float 16.34 pi () (expt 10 3))
)
(length lispziger-allerlei) => 7
Die Funktion
(length) gibt die Länge einer Liste zurück,
d.h. die Anzahl der Listenelemente. Unterlisten zählen als ein
Element. Schön und gut - aber was kann man noch mit Listen
machen? Denken Sie einen Moment nach und schreiben Sie einen
Wunschzettel.
Der Wunschzettel könnte so aussehen:
- Feststellen, ob ein bestimmtes Element in der Liste vorhanden ist
- Ein neues Element in die Liste einfügen
- Ein Element aus der Liste entfernen
- Feststellen, ob Elemente doppelt vorkommen
- die Liste alphabetisch sortieren
- die Liste auf dem Drucker ausgeben
- die Liste als Text in die Zeichnung einfügen
Und nun kommen die Antworten auf Ihren Wunschzettel, die
wahrscheinlich erst einmal etwas ernüchternd wirken werden:
Es gibt eine Funktion
(member), die dazu dient,
festzustellen, ob ein bestimmtes Element in einer Liste
vorhanden ist. So wird sie benutzt:
(member "Hemd" lispziger-allerlei)
=> ("Hemd" FLOAT...usw.)
(member 'FLOAT lispziger-allerlei)
=> (FLOAT 16.34...usw)
(member) gibt immer den Listenrest beginnend mit dem gesuchten
Element zurück. Wenn das Element nicht enthalten ist, wird
nil
zurückgegeben. Vergessen Sie jedoch nicht, das gesuchte Element
zu quoten, da LISP sonst versucht dieses zu evaluieren ("Hemd"
ist ein Sonderfall, da es wegen der Anführungszeichen sowieso
nicht evaluiert werden kann).
Sie können jederzeit ein neues Element vor den Anfang der Liste
setzen - und zwar mit der Funktion
(cons).
(cons 'neues_element lispziger-allerlei)
=> (NEUES_ELEMENT 1 "Hemd" usw.)
Mit einem kleinen Umweg können Sie auch ein neues Element an
das Ende der Liste anfügen. Dazu dient die Funktion
(append).
Diese Funktion kann allerdings nur zwei oder noch mehr Listen zu
einer neuen Liste zusammenfügen, d.h. ein Aufruf in der Art
(append vorhandene-Liste neues-element) ist zum Scheitern
verurteilt. Wir müssen also auch noch die Funktion
(list)
zu Hilfe nehmen, mit der man eine Liste erzeugen kann. Zunächst
zu
(list):
(setq erzeugte-liste
(list 'ein_element 'noch_ein_element)
) => (EIN-ELEMENT NOCH-EIN-ELEMENT)
Beachten Sie, daß jetzt jedes Element einzeln gequotet werden
muß. Wenn wir nun das Element neues_element am Ende von
lispziger-allerlei einfügen wollen, müssen wir das so
anstellen:
(append lispziger-allerlei (list 'neues_element))
Lisp antwortet uns nun wunschgemäß mit der Rückgabe:
=> (1 "Hemd" FLOAT 16.34 PI () (EXPT 10 3) NEUES_ELEMENT)
aber wenn wir jetzt
!lispziger-allerlei eingeben, bekommen
wir doch wieder nur die alte Version ausgegeben:
=> (1 "Hemd" FLOAT 16.34 PI () (EXPT 10 3))
Ja richtig - da war doch was! Genau - wir müssen zwischen Rückgabe
und Zuweisung eines Wertes an ein Symbol unterscheiden. Erst die
Eingabe von
(setq lispziger-allerlei
(append lispziger-allerlei
(list 'neues_element)
)
)
verleiht der Sache endgültigen Charakter.
Das Entfernen eines Elements aus einer Liste ist in AutoLISP
leider keine ganz einfache Angelegenheit. Es gibt weder eine
direkte noch eine Umweg-Funktion. Man muß die ganze Liste unter
Auslassung des zu entfernenden Elements in eine neue Liste
umkopieren. Auch die Antwort auf diese Frage setzt ein fundiertes
AutoLISP-Wissen voraus und kann erst später gelöst werden. Das
Sortieren einer Liste setzt auf jeden Fall voraus, daß die
Liste einen einheitlichen Datentyp enthält (also entweder Zahlen
oder Zeichenketten).
Andere Datentypen lassen sich nicht (oder kaum) sinnvoll
sortieren. In Erwägung zu ziehen wären noch Listen, die
ausschließlich Listen enthalten. Diese wären dann sortierbar,
wenn die Unterlisten ausschließlich Zahlen oder Zeichenketten
enthielten. Solche Listen können z.B. geometrische Punkte
enthalten, diese werden immer als Liste mit drei Zahlen (X-, Y-
und Z-Koordinate) dargestellt.
Die Möglichkeit des Druckerzugriffs besteht in AutoLISP
überhaupt nicht. AutoLISP gestattet keinerlei Hardware-Zugriffe
außer dem Lesen und Schreiben von Dateien (in den neuesten Versionen
noch etwas mehr, wie z.B. das Löschen). Diese Beschränkung
läßt sich nicht umgehen - auch nicht durch noch so fundierte
LISP-Kenntnisse.
Die Ausgabe von Listen in die Zeichnung (als Text) stellt
einen Programmierer vor nicht allzu große Probleme. Uns fehlt
zwar jetzt noch das notwendige Handwerkszeug, wir werden es aber
bald behandeln und sollten dann eine solche Aufgabenstellung
leicht lösen können.
Nun aber zurück zu den Befehlen zur direkten Bearbeitung von
Listen. Wir haben
(length) kennengelernt, mit dem man
die Länge einer Liste ermitteln kann, ferner
(cons) und
(append). Mit
(cons) kann man ein Element vor eine
Liste setzen, und
(append) fügt zwei oder noch mehr
Listen zusammen. Wir sollten jetzt aber noch einmal auf
(cons)
eingehen, da es eine etwas komplexere Funktion ist. Wenn wir den
folgenden Ausdruck eingeben:
(cons "gelb" '("rot" "grün" "blau"))
dann sehen wir, daß im Normalfall das erste Argument ein Atom
und das zweite eine Liste sein muß. Stopp! Der Begriff Atom ist
doch bisher gar nicht gefallen. Ganz einfach - ein Atom ist etwas,
wenn es keine Liste ist. Liste ist also das Gegenteil von Atom
und umgekehrt. Eine Kleinigkeit stört aber doch wieder: Die
leere Liste - nil - ist sowohl eine Liste als auch ein Atom. Wir
haben also eine einzige Ausnahme, die die Regel aber doch nur
bestätigt.
Die Funktion
(append) verlangt als Argumente ausschließlich
Listen. Jeder Versuch, ihr ein Atom unterzumogeln, endet mit
einer Fehlermeldung. Bei
(cons) ist das anders. Das erste
Argument kann sowohl ein Atom als auch eine Liste sein. Im
letzten Beispiel war es das Atom "gelb". Setzen wir als erstes
Argument eine Liste, dann wird diese Liste als Unterliste an
den Anfang gesetzt.
(cons '("lila" "gelb") '("rot" "grün" "blau"))
ergibt also (("lila" "gelb")"rot" "grün" "blau"). Die Länge
dieser so erzeugten Liste ist 4. Hätten wir einfach eine Liste
mit den 5 Farben gewollt, dann hätten wir eben
(append)
bemühen müssen und nicht
(cons).
(cons) bietet
aber noch eine andere Möglichkeit, die wir jetzt nur kurz
erwähnen können. Wir werden später in einem eigenen Abschnitt
darauf eingehen. Wenn bei
(cons) das zweite Argument ein
Atom ist, dann ...
Probieren wir es doch einfach aus:
(cons "rot" "grün") => ("rot" . "grün")
Was da herausgekommen ist, nennt sich ein 'dotted pair' - ein
gepunktetes Paar. Diese dotted pairs sind für viele tatsächlich
ein so erfreulicher Anblick wie ein von Windpocken, Masern und
der Beulenpest gleichzeitig heimgesuchtes Pärchen chinesischer
Nackthunde. Und was das Dumme daran ist: Sie sind nicht etwa so
selten wie sechs Richtige im Lotto - nein, fast die gesamte
AutoCAD-Geometriedatenbank besteht aus dotted pairs. Warum
sollten wir etwas gegen diese Dinger haben, werden Sie sich
jetzt fragen. Antwort kommt sofort - versuchen Sie folgende
Eingaben:
(cons 'a '(b . c)) => Fehlermeldung
(length '(a . b)) => ebenso
(append '(a . b)'(c . d)) => raten Sie mal...
Es sind also keine Listen? Versuchen Sie es mit
(listp).
Das ist eine sogenannte Prädikatfunktion; sie testet, ob etwas
eine Liste ist.
(listp '("rot" . "blau")) => T
T bedeutet hier soviel wie 'Ja' oder 'wahr' (T steht für "true",
listp für 'list predicate'). Vielleicht noch eine Ausnahme,
die gleichzeitig Liste und Atom ist? Nein, versprochen ist
versprochen, außer
nil gibt es keine Ausnahmen mehr. Sie können
das auch testen, die Funktion heißt
(atom). Sie sollte
eigentlich
(atomp) für 'atom predicate' heißen, daß p am
Ende ist aber offensichtlich irgendwann irgendwo auf der Strecke
geblieben.
Egal - wir stellen fest, es handelt sich bei den schwerkranken
chinesischen Nackthunden nicht um Atome, sondern wirklich um
Listen. Trotzdem versagen fast alle Listenbefehle an ihnen, und
damit müssen wir leben.
Übrigens, wenn das Ergebnis einer Test- oder Prädikatfunktion
negativ ausfällt, dann wird statt des T nicht etwa F (false)
oder G (gelogen, gemogelt) oder so etwas ausgegeben, sondern
nil. Die leere Liste muß also doch für einiges herhalten.
Dazu später mehr...
Wir lassen alle diese hochinteressanten Fragen jetzt doch erst
mal dahingestellt und befassen uns noch ein wenig mit
(append) und
(cons). Hüten Sie sich vor der
Annahme, daß dies zwei ähnliche Funktionen sind, so etwa nach
dem Motto:
(cons) nehmen wir, wenn wir etwas vorne
davorsetzen wollen, und
(append) kommt dan dran, wenn wir
etwas hinten ankleben wollen.
Das ist so falsch, wie es falscher nicht sein kann!
(cons) fügt Elemente ein, und
(append) verbindet
zwei Listen. Wenn uns bei
(append) die Lokalitäten nicht
passen (hinten), dann müssen wir doch nur die Argumente vertauschen:
(append '("blau" "gelb")'("rot" "grün"))
=> ("blau" "gelb" "rot" "grün")
(append '("rot" "grün")'("blau" "gelb"))
=> ("rot" "grün" "blau" "gelb")
Schwieriger wird es bei
(cons). Wir können nicht einfach
die Argumente vertauschen, da sonst statt der Liste, die wir uns
vorstellen, ein chinesischer Nackthund entsehen könnte.
Trotzdem - wir können auch mit
(cons) ein Element hinten
anhängen. Wir nehmen einfach den Umweg über
(reverse).
Diese Funktion dreht eine Liste einfach um - Liste rückwärts.
(reverse '("rot" "grün" "blau"))
=> ("blau "grün" "rot")
Und wie soll das jetzt mit
(cons) und
(reverse)
gehen? Wir wollten doch nur ein Element hinten anhängen! So
geht das:
(reverse(cons "rot"(reverse '("gelb" "grün" "blau"))))
=> ("gelb" "grün" "blau" "rot")
Wir hätten das - Sie haben das doch gewußt! - aber auch mit
(append '("gelb" "grün" "blau")'("rot"))
=> ("gelb" "grün" "blau" "rot")
erreicht. Jedenfalls kennen wir jetzt auch
(reverse).
Wir sollten uns jetzt aber noch einer Gruppe von Funktionen
zuwenden, die fast das Gegenteil von dem bewirkt, was wir
bisher so gemacht haben. Bis jetzt haben wir uns damit
befaßt, die Listen zu erstellen und immer mehr Elemente
hinzuzufügen. Die Frage ist jetzt: Wie kommen wir an
einzelne Elemente in Listen heran? Darum geht es im nächsten
Kapitel.
Übungsaufgaben
-
-