Lisp für Anfänger – Alles ist eine Liste

Da ich in letzter Zeit schon mehrfach Wundersames über die Programmiersprache Lisp gelesen habe, wollte ich nun endlich selbst ausprobieren, was es damit auf sich hat. Bei Lisp denken die meisten erstmal an furchtbar viel Mathe oder an eine uralte Sprache, die heute wohl kaum noch einer benutzt. Richtig ist, daß der Kreis der Lisp-Programmierer eher eine verschworene kleine Gemeinschaft als eine große Mainstream-Bewegung ist und der Ursprung der Sprache basiert ganz klar auf mathematischen Grundlagen. Trotzdem ist der Einstieg nicht schwer und jeder kann das mit einfachen Mitteln ausprobieren. Die schlichte Schönheit der Sprache lässt sich dabei schnell erkennen.

lisplogo

Paul Graham sieht die nach wie vor geringe Verbreitung von Lisp als Wettbewerbsvorteil für Lisp-Programmierer an. Konkurrenten müssten nach Graham in sogenannten „Blub-Sprachen“ programmieren und wären dadurch weit weniger flexibel in der Entwicklung. Dies ist natürlich recht überheblich formuliert, allerdings betonen doch eigentlich alle Lisp-Programmierer, welche Vorteile Lisp gegenüber allen anderen Sprachen hat und warum eben diese anderen Sprachen niemals genau diese Vorteile haben werden. Es sei denn, man baut sie zu Lisp um 🙂 Jeder mag natürlich die Sprache die er am besten kann am liebsten, dennoch lohnt es sich eigentlich immer über den Tellerrand hinauszuschauen.

john_mccarthyLisp wurde 1958 erstmals von John McCarthy am Massachussets Institute of Technology (MIT) als theoretische Erforschung einer mathematischen Syntax spezifiziert. Einer seiner Studenten implementierte dies schließlich als Programmiersprache. Seitdem wurden zahlreiche Lisp-Dialekte entwickelt, doch die Basis blieb stets dieselbe. Nach Fortran ist Lisp die zweitälteste Programmiersprache, die noch in Verwendung ist.

Der Begriff Lisp bedeutet „List Processing“  (Listen-Verarbeitung) und tatsächlich ist in Lisp alles eine Liste. Listen können beliebig verschachtelt sein und es läßt sich alles damit ausdrücken. So sind auch Programmanweisungen Listen, wobei das erste Element der Liste die auszuführende Funktion darstellt. Dies kann zum Beispiel ein Operator sein, wie bei Addition oder Multiplikation:

(* 3 (+ 2 5))

Wer als Ergebnis 21 herausbekommt, hat das Prinzip schon verstanden 🙂

Da Lisp dem Entwickler größtmögliche Flexibilität und Einflussnahme ermöglicht, wird es häufig auch als programmierbare Programmiersprache bezeichnet.

Diese Programmierbarkeit von Lisp hängt direkt von der Verwendung von S-Expressions  (Symbolic Expressions) ab, mit welchen sowohl Programme als auch Daten dargestellt werden. Damit lassen sich auch Programmteile zur Laufzeit beliebig manipulieren. Dies wird vor allem mit sogenannten Makros erreicht, kleinen Lisp-Programmen, mit welchen sich der Programmcode selbst verändern lässt, bevor dieser vom Interpreter/Compiler verarbeitet wird und durch welche die Sprache ihre unglaubliche Flexibilität und Eleganz erhält. Der dafür zuständige Makroprozessor, sucht vor der Ausführung des Codes nach Makros und wandelt diese in regulären Lisp-Code um. Erst danach werden die Programmfunktionen ausgeführt.

Grundlagen der Lisp Syntax

Die Lisp-Syntax besteht grundsätzlich aus Symbolen, Zahlen und Strings, die sich in beliebig verschachtelten Listen befinden. Ein Symbol ist ein alleinstehendes Wort, das aus Buchstaben, Zahlen und bestimmten Sonderzeichen bestehen kann. Um zwischen Programmcode und Daten unterscheiden zu können, wird Daten ein ‚ vorangestellt, damit sie nicht als Programmcode ausgeführt werden.

Listen in Lisp bestehen aus einzelnen „Cons“-Zellen, die miteinander verknüpft sind. Um Listen zu manipulieren gibt es die Funktion cons, um 2 Elemente zu verknüpfen, die Funktion car, um auf das erste Listenelement zuzugreifen und cdr, um den Rest der Liste zu bekommen. Diese letzten beiden lassen sich auch verknüpfen um bestimmte Elemente der Liste zu bekommen, z.B. cadr (zweites Element) oder caddr, cddar, cadadr (try it out:)). Nil oder 0 ist immer die leere Liste. Dies entspricht auch dem boolean-Wert false. Rekursion wird dadurch sehr einfach, da nur das erste Element entnommen werden muss und anschließend der Rest wieder der Funktion übergeben werden kann bis die Liste leer ist.

(cons 'rot '(blau gelb))
=>(ROT BLAU GELB)

(car '(rot blau gelb))
=> ROT

(cdr '(rot blau gelb))
=>(BLAU GELB)

Globale Funktionen werden in Lisp mit dem bereits von der Sprache vorgegebenen Makro defun definiert, globale Variablen mit defparameter. Um globale Variablen besser erkennbar zu machen, werden sie per Konvention von Sternchen („earmuffs“) eingeschlossen. Mit setf können globale Variablen verändert werden.

(defun square(x)
    (* x x))

(defparameter *small* 1)

(setf *small* 5)

Lokale Variablen und Funktionen werden mit let bzw. flet definiert:

(let ((a 5)
      (b 6)) => Variablen werden deklariert
  (+ a b)) => Variablen werden angewendet

(flet ((f(n)
         (+ n 10))) => Funktion wird definiert
  (f 5)) => Funktion wird aufgerufen

Etwas lästig sind beim Programmieren von Lisp die vielen vielen Klammern, weshalb die Abkürzung LISP auch scherzhaft als „Lots of Irritating Superfluous Parentheses“ (eine Menge lästiger, überflüssiger Klammern) interpretiert wird 🙂

lisp_cycles

 

 

 

 

Lisp-Entwicklungsumgebung installieren

Um Lisp auszuprobieren muss zunächst die OpenSource-Entwicklungsumgebung CLISP von http://clisp.org heruntergeladen und installiert werden (gibt es für alle Plattformen). Um CLISP zu starten, einfach clisp in die Kommandozeile eingeben.

Probiere einige Befehle aus. Nach der Eingabe auf Enter drücken und das Ergebnis wird in der nächsten Zeile ausgegeben. Die Clisp-Konsole gibt immer automatisch den Rückgabewert einer Funktion aus. Kommentaren wird ein ; vorangestellt.

(+ 3(* 2 4))

;Definiere eine Funktion, die ihr Argument quadriert:
(defun square (x)
  (* x x))

;Quadriere die Zahl 3: 
(square 3)

;Setze die Variable pi auf den Wert 3,1415: 
(setf pi 3.1415)

(+ pi 2)

Wenn man einen Fehler macht und Clisp sich komisch verhält einfach :q eingeben und es wird repariert. Beende Clisp mit der Eingabe von quit.

Lisp ausprobieren

In dem Buch „Land of Lisp“ wird gleich zu Beginn ein kleines Kommandozeilen-Programm vorgestellt, daß die Funktionsweise von Lisp verdeutlicht. Dies sei hier aufgeführt, damit ihr was zum Ausprobieren habt.

Gebe nacheinander die folgenden Befehle in Clisp ein:

;Definiere die zwei globalen Variablen small and big
(defparameter *small* 1)
(defparameter *big* 100)

;Definiere die globale Funktion guess-my-number, die anhand der 
eingebauten arithmetischen Shiftfunktion ash, den Durchschnitt zwischen 
der größten und der kleinsten Zahl ausrechnet.
(defun guess-my-number ()
  (ash (+ *small* *big*) -1))

;Definiere die globalen Funktionen smaller und bigger, die die globalen 
Variablen um 1 erhöhen oder erniedrigen und dann erneut den Durchschnitt ausrechnet.
(defun smaller ()
  (setf *big* (1- (guess-my-number)))
  (guess-my-number))

(defun bigger ()
  (setf *small* (1+ (guess-my-number)))
  (guess-my-number))

;Definiere die Funktion start-over um die Variablen wieder zurückzusetzen
(defun start-over ()
  (defparameter *small* 1)
  (defparameter *big* 100)
  (guess-my-number))

Um das Spiel auszuführen, wähle eine Zahl zwischen 1 und 100. Rufe die Funktion (guess-my-number) auf (Funktionsnamen immer mit Klammern angeben). Als erstes Ergebnis wird immer die 50 als Durchschnitt angegeben. Im Anschluss wird entweder (smaller) oder (bigger) aufgerufen, bis die richtige Zahl erraten ist. Am Ende kann das Spiel mit (start-over) auf die Startwerte zurückgesetzt werden. Kein sehr trickreiches Spiel, aber zumindest hat man schonmal ein paar Funktionen und Variablen angewendet 😉

Links

Schreibe einen Kommentar