Zurück zur Startseite Fahrrad Fotografie Diverses Die Galaxis
Computer Linux Age of Kings Age of Mythology [TSD]-Clan

Common Lisp

Powered by LISP

In diesem Artikel will ich etwas näher als in der Übersicht auf Lisp im Allgemeinen und Common Lisp im Besonderen eingehen. Leider bin ich weit entfernt davon, ein Lisp Experte zu sein, nichtsdestotrotz kann vielleicht der eine oder andere Leser ein paar Informationen herausziehen, die für ihn nützlich sind. Besonders natürlich diejenigen, die Lisp nur vom Hörensagen kennen.
Allerdings soll das hier kein Kursus werden. Wer Lisp lernen will, kann unten auf dieser Seite in der Literaturliste nachschauen.

Präfixnotation

Was vielen Programmierern sauer aufstößt bei Lisp, ist die konsequente Verwendung der Präfixnotation. Beispielsweise in der Addition, hier der Zahlen 4 und 6, deren Summe einer Variablen x zugewiesen wird:

(setf x (+ 4 6))
Das sieht zum Beispiel in Java mit der Infixnotation doch viel gewohnter aus:
x = 4 + 6;
Aber halt: Die Addition ist ja nun eine der einfachsten Operationen, die es so gibt. Wenn man etwas schwierigeres machen möchte, zum Beispiel eine Potenzierung (also x gleich 4 hoch 6), wie sieht es dann aus? In Common Lisp so:
(setf x (expt 4 6))
Und in Java:
x = Math.pow(4d, 6d);
Tja, wo ist denn nun die schöne Infixnotation geblieben? Sobald es komplizierter wird, weichen nämlich fast alle Sprachen ebenfalls auf die Präfixnotation aus (spätestens bei der Deklaration eigener Funktionen). Lisp ist da einfach konsequenter und verwendet diese Art immer. Dadurch muß man nicht lange überlegen, bei einem Funktionsaufruf ist die Funktion immer vorne in der Klammer.

Klammern

Das fällt wohl jedem auf: In Lisp wimmelt es nur so vor Klammern. Viele Anfänger weisen das brüsk zurück: "Ich will doch nicht Klammern zählen müssen!"
Die Gute Nachricht: Muß man auch nicht. Ein Lispprogrammierer liest das Programm durch die Einrückungen, die beim Erstellen von einem guten Texteditor automatisch richtig gemacht werden. Der gute Editor zeigt bei der Eingabe auch immer die zugehörigen Klammerpaare an und so zählt kein Lispprogrammierer Klammern.
Man braucht also nur einen guten Editor. Es gibt spezielle Lisp-IDEs, unter den normalen Texteditoren ist Emacs besonders geeignet. Denn Emacs ist selbst zu großen Teilen in Lisp geschrieben und bringt ein eigenes Lisp mit.

Hat man sich erst einmal an die vielen Klammern gewöhnt, stören sie kaum noch.

Listen

Die wichtigste Datenstruktur in Lisp ist die Liste. Es gibt auch andere, Hashtabellen oder Arrays oder Strings. Aber Listen sind das Markenzeichen von Lisp, kommt der Name schließlich von List Processing.
Das geht so weit, daß jedes Lisp Programm selbst auch wieder eine Liste ist und damit von anderen Programmen als Eingabe verarbeitet oder als Ausgabe erzeugt werden kann.

Hierzu ein kleines Beispiel, das zwar relativ sinnlos ist, aber wenigstens anschaulich:

(setf willi (+ y (* x 5)))
Es wird also einer Variablen willi der Wert zugewiesen, der sich aus der Berechnung (x * 5) + y ergibt. Doch mit einer kleinen Änderung wird etwas ganz anderes daraus:
(setf willi '(+ y (* x 5)))
Was hat sich geändert? Es ist nur ein kleines Apostroph dazugekommen. Aber das bewirkt, daß der dahinterliegende Ausdruck nicht mehr ausgewertet wird. Statt einer Zahl, die sich aus einer Berechnung ergibt, ist willi jetzt eine Liste, die diese Berechnung durchführt, also ein Programm. Um diese Berechnung ablaufen zu lassen, kann man beispielweise so vorgehen:
(eval willi)
OK, so würde man das nicht wirklich machen (vor allem ist eval langsam), aber das Prinzip stimmt: Programme und Daten sind für Lisp sozusagen das gleiche.

Interaktiv - interpretieren oder compilieren?

Lisp ist eine interaktive Programmiersprache. Das bedeutet, man schreibt zum Beispiel eine Funktion und kann sie sofort verwenden, ohne langen Compilierzyklus. So kann man jeden Programmbestandteil schnell testen und nicht erst, wenn das Programm als Ganzes fertig ist.
Aus diesem Grund ist Lisp besonders gut geeignet für die sogenannte Bottom-Up Programmierung. Dabei fängt man mit den kleinen, einfachen Dingen an und baut aus schon fertigen Bestandteilen dann kompliziertere.

Weil Lisp interaktiv ist, glauben viele Leute, daß Lisp eine Interpretersprache ist. Tatsächlich kann man aber in Lisp natürlich auch compilieren, und zwar nicht nur das ganze Programm, sondern auch kleine Bestandteile. So wird der Zyklus Schreiben-Testen-Verbessern nicht unnötig lange unterbrochen.

Funktionen und funktionale Programmierung

Funktionen sind in Lisp (wie in den meisten anderen Sprachen auch) sehr wichtig. Das ist ja nun nichts ungewöhnliches, interessant wird es aber hier: In Lisp können Funktionen andere Funktionen als Parameter bekommen oder als Wert zurückliefern. Das geht in C zwar auch, aber nur über Zeiger.

Auch nicht uninteressant: Lisp Funktionen können beliebig viele Werte zurückgeben. Und zwar ohne den Einsatz von Tricks wie zum Beispiel einer Klasse oder Struktur als Rückgabewert.
Ebenso gibt es in Lisp anonyme Funktionen, die also keinen Namen haben und sozusagen an Ort und Stelle definiert werden. Das kann sinnvoll sein, wenn diese Funktion nur einmal benötigt wird.

Kommen wir zur berühmten funktionalen Programmierung. Was hat es damit auf sich?
Funktionale Programme arbeiten, indem sie Werte aus Funktionen zurückliefern, anstatt Dinge zu verändern. Beispielsweise irgendwelche Variablen zu manipulieren, die nicht lokal in der Funktion deklariert sind. In der nicht-funktionalen, sogenannten imperativen Programmierung ist nicht die Anwendung einer Funktion auf Daten der Kernpunkt, sondern Arbeitsanweisungen an den Prozessor.
Funktionale Programmierung kommt also ohne die sogenannten Seiteneffekte aus. Ein Beispiel: Wir schreiben eine Funktion, die eine Liste bekommt als Parameter und diese Funktion soll die Liste zurückliefern ohne das mittlere Element. Mit Seiteneffekten würde die Funktion also die ihr übergebene Liste manipulieren. Ohne Seiteneffekt (also funktional programmiert) bekommt man eine neue Liste, die ursprüngliche bleibt unverändert.

Dadurch sind verschiedene Funktionen deutlich unabhängiger voneinander. Man muß sich nicht erst überlegen, welche Änderungen die Funktion, die man aufrufen will, an irgendwelchen Objekten vornimmt, denn sie nimmt keine vor.
Es ist schwer durchzuhalten, ein größeres Programmpaket streng funktional zu programmieren. Aber man sollte es wenigstens versuchen und Lisp bietet dazu gute Möglichkeiten.

Makros

Bei C-Programmiern haben Makros nicht unbedingt den besten Ruf, aber Lisp Makros sind ein ganz anderes paar Schuhe. In C sind Makros primitive Ersetzungsanweisungen an den Präprozessor des Compilers. In Lisp kann man sich mit Makros eine eigene Programmiersprache innerhalb von Lisp erzeugen.

So gibt es in Common Lisp keine for-Schleife. Für Schleifen gibt es die do Konstruktion, die aber nicht unbedingt einfach ist und meistens visuellen Overkill darstellt. Also kann man sich ein Makro schreiben, das eine for-Schleife einführt (entnommen aus "On Lisp" von Paul Graham):

(defmacro for ((var start stop) &body body)
  (let ((gstop (gensym)))
    `(do ((,var ,start (1+ ,var))
          (,gstop ,stop))
	 ((> ,var ,gstop))
      ,@body)))
Und schon gibt es in unserem Lisp eine for-Schleife. Das ist nicht einfach eine Funktion, sondern beim Übersetzen des Programms wird dieses Makro expandiert und in eine andere Befehlsfolge umgewandelt.
Makros können noch viel komplexer sein als dieses Beispiel, es ist ein weitaus leistungsfähigerer Mechanismus als ein einfaches Suchen und Ersetzen. Aus diesem Grund wird Lisp auch als programmierbare Programmiersprache bezeichnet.

Literatur

Das schon erwähnte On Lisp, darin wird vor allem auf die Erstellung von Makros eingegangen.

Ebenfalls von Paul Graham ist "ANSI Common Lisp" mit ausführlicher Referenz von Common Lisp (ISBN: 0-13-370875-6).

Ein besonders einsteigerfreundliches, praxisnahes und sehr neues Buch von Peter Seibel ist Practical Common Lisp.

Leider scheint es wenige deutschsprachige Bücher zu Common Lisp zu geben, wer ein gutes und aktuelles kennt, möge mir eine Mail schreiben.

Zurück zum Seitenbeginn Zurück zu Programmiersprachen