Ein paar Worte vorabHome   Letzte MeldungenNews   Index der Kapitel und der besprochenen FunktionenIndex   Wer ich bin, warum ich diese Seiten mache, KontaktImpressum   Ich freue mich über jeden Eintrag im Gästebuch!Gästebuch   Einige Links zu anderen AutoLisp-SeitenLinks   Copyrights und DisclaimerRechts
Hier können die kompletten Seiten als ZIP-File heruntergeladen werden!

Berechnen von arithmetischen Ausdrücken in der Kommandozeile Sitz!Platz!Fass!
Das Verschachteln von Ausdrücken Alte Schachtel!
Das Speichern von Werten in Variablen Gebunkert
Verhindern der Evaluation mit Quote Bergbäche
Erzeugen von einfachen Listen in AutoLisp Brot,Eier,Käse
Einfache Funktionen zur Listenbearbeitung ...um die Wurst
Funktionen für den Zugriff auf Listenelemente Was ein Salat!
Über Haupt- und Nebeneffekte von Funktionen Schwer schuften
Das Definieren von eigenen Funktionen in AutoLisp Ostfriesischer...
Lokale Variablen und Funktionsargumente in AutoLisp Kondome!
Das Laden von Programmdateien in AutoLisp Banküberfall
Verzweigung in Funktionen aufgrund von Entscheidungen Wenn das Wort...
Zusammenfassen von Entscheidungen mit den Logik-Funktionen Ins Schweinderl
Mehrfach-Verzweigungen in AutoLisp mit Cond 3. Strasse links
Schleifen zum Steuern des Ablaufs in AutoLisp-Funktionen Wie im Fernsehen
Testfunktionen zum Steuern von Schleifen in AutoLisp Schwanger?
Gleichheitstests als Schleifenkriterium in AutoLisp Noch gleicher?
Zeichneketten-Bearbeitung in AutoLisp Rauchzeichen
Funktionen zur Konvertierung von Datentypen in AutoLisp Wasser zu Wein
Komplexere Funktionen für die Bearbeitung von Listen in AutoLisp Nicht arbeiten...
Das Erzeugen von anonymen Funktionen mit lambda Schwarze Kutte
Das Bearbeiten von Listenelementen mit foreach Jedem das Seine
Erzeugen und Verwenden von Assoziationslisten in AutoLisp Beim Psychiater
Zugriff auf Geometriedaten und Erzeugen von Geometrieelementen Ententanz
Der Umgang mit Auswahlsätzen in AutoLisp Jung gefreit, ...
Auswahl von AutoCAD-Zeichnungsgeometrie mit ssget Raffgierig!
Verändern von Zeichnungs-Geometrie mit entmod Flickschusterei
Das Erzeugen von Geometrie mit entmake Houdini
Über Programmierstile in AutoLisp, Teil 1 Emma
Über Programmierstile in AutoLisp, Teil 2 Sti(e)lblüten
Über Programmierstile in AutoLisp, Teil 3 Eingewickelt
Über Programmierstile in AutoLisp, Teil 4 Doofe Frisur?


Zum den Seiten für Fortgeschrittene

Zu den ActiveX-Seiten

Meine Private HP mit Fotos, Gedichten, Musik und Postkartenversand

Mein Online-Lexikon der Fotografie

Mein völlig abgedrehtes Reisebüro










In diesem Kapitel zählen - wie leider meistens im Leben - nicht die inneren Werte, sondern nur die Äusserlichkeiten! Warum sollte das wichtig sein - reicht es denn nicht, wenn der Code fehlerfrei läuft? Manchmal schon - aber auch fehlerfreie Programme bleiben nicht davon verschont, dass Änderungen fällig werden.

Es beginnt damit, dass sich mit jedem neuen AutoCAD-Release die bange Frage stellt, ob bisherige Lisp-Programme ohne Anpassung weiterlaufen, oder ob Änderungen fällig werden. Bei meinen Programmen war es meistens so, dass sie nicht ohne kleine oder grössere Änderungen weiterlaufen wollten. Nicht etwa, weil AutoLisp grössere Umstellungen erfahren hätte - nein es waren ganz banale Kleinigkeiten, so klein, dass AutoDESK sie vielleicht nicht einmal selber wahrgenommen hat.

Bei der Umstellung vom alten AutoLisp auf den neuen VisualLisp-Interpreter hat es allerdings ganz massive Änderungen gegeben. Auch hervorragend funktionierende Programme müssen also geändert werden - und wenn es nicht an einem neuen Release liegt, dann vielleicht daran, dass die Qualität eines Programms den Appetit des Auftraggebers geweckt hat (vielleicht aber auch den des Programmierers).

Das Programm, an dem man gerade arbeitet, versteht man ja in der Regel auch hervorragend. Ist das Programm aber ein paar Monate oder vielleicht sogar Jahre alt, sieht die Sache anders aus: man muss sich neu hineindenken. Das fällt bei einem gut strukturierten, ebenso gut kommentierten und sauber geschriebenen Programm recht leicht, aber bei einem Wust von unsauber formatiertem Spaghetti-Code kann es sehr schwer bis nahezu unmöglich werden.

Befassen wir uns zunächst einmal mit dem Einrücken: Dies ist einfach notwendig, um auf einen Blick zu sehen, welche Funktion welche Argumente bekommt und wo der Funktionsaufruf zu Ende ist. Prinzipiell zweifelt an dieser Notwendigkeit niemand - aber trotzdem findet man im Internet unglaublich viel AutoLisp-Code, der überhaupt nicht eingerückt ist. So viel übrigens, dass sich Programmierer-Kollegen aus anderen Sprachen schon mal über die AutoLispler lustig machen.

Kaum ein AutoLisp-Programmierer ist über andere Lisp-Dialekte zu AutoLisp gekommen - fast alle haben zuerst mit AutoCAD gezeichnet und dann angefangen, ein bisschen mit AutoLisp zu 'spielen'. Daher ist es auch kein Wunder, dass die AutoLisp-Welt eine ganz andere ist als die übrige Lisp-Welt.

Insbesondere beim Einrücken haben sich zwei völlig unterschiedliche Stile herausgebildet. Zum Vergleich eine Funktion in mehreren Varianten, die sich nur durch das Einrücken unterscheiden:
; der CommonLisp-Stil
(defun fakultaet(zahl / )
  (if(= zahl 1)
    1
    (* zahl(fakultaet(1- zahl)))))

; der AutoLisp-Stil
(defun fakultaet(zahl / )
  (if(= zahl 1)
    1
    (* zahl(fakultaet(1- zahl)))
  )
)

; der halbherzige Stil
(defun fakultaet(zahl / )
  (if(= zahl 1)
    1
    (* zahl(fakultaet(1- zahl)))
) )

; gar kein Stil
(defun fakultaet(zahl / )
(if(= zahl 1)
1
(* zahl(fakultaet(1- zahl)))
)
)

; chaotischer Stil
(defun fakultaet(zahl / )
(if(= zahl 1)
    1 (* zahl
 (fakultaet(1- zahl)
          ))
)
  )
                  
Der sog. CommonLisp-Stil ist natürlich nicht nur bei CommonLisp üblich, sondern bei allen Lisp-Dialekten ausser AutoLisp. Ich nenne ihn nur einfach so, damit das Kind einen Namen hat - CommonLisp ist einfach der am weitesten verbreitete Dialekt. Dieser Einrückstil ist kompakter als der AutoLisp-Stil, bei dem schliessende Klammern oft allein auf einer Zeile stehen, insbesondere am Ende von Funktionen. Dass die beiden letzten Beispiele nur abschreckend sein können, liegt auf der Hand - deshalb gehe ich auch gar nicht weiter darauf ein.

Es ist also eine Entscheidung treffen zwischen den beiden ersten Beispielen (auf den halbherzigen Stil gehe ich noch ein!). Vorschriften will ich da natürlich niemandem machen - aber ich plädiere eher für den AutoLisp-Stil. Er ist zwar etwas umständlicher und verlängert den Code, aber er hat für mich einen großen Vorteil: Man kann mehr oder weniger auf einen Blick die Klammern zuordnen, denn jede öffnende Klammer steht entweder weiter rechts in der selben Zeile, oder aber genau senkrecht darunter (wenn auch evtl. einige Zeilen weiter unten).

Der 'halbherzige' Stil versucht, beide Vorteile miteinander zu verbinden. Die schliessenden Klammern stehen exakt in der selben Spalte wie die öffnenden - aber Vorsicht: Wenn man genau hinsieht, bemerkt man, dass sie vertauscht werden! Das (defun) wird in Spalte 1 auf- und in Spalte 3 zugemacht, das (if) wir in Spalte 3 auf- und in Spalte 1 zugemacht. Das jedoch macht einem das Leben wirklich schwer, wenn man mit einem Editor mit automatischer Klammerprüfung arbeitet.

Der AutoLisp-Stil hat für mich noch einen weiteren Vorteil gegenüber dem CommonLisp-Stil: Es macht es wesentlich einfacher, mit Copy/Paste Programmzeilen einzufügen bzw. herauszunehmen. Beim CommonLisp-Stil ist man da sehr oft darauf angewiesen, die Klammern regelrecht abzuzählen. Deshalb plädiere ich für den AutoLisp-Stil, auch wenn er von anderen Lispern manchmal als Anfänger-Stil belächelt wird. Noch wichtiger als die Frage, für welchen der beiden Stile man sich entscheidet, ist die Frage, ob man sich überhaupt zu etwas entschliesst. Werfen Sie noch einmal einen Blick auf das letzte Beispiel: Schlamperei war noch nie ein Zeichen von Professionalität.

Eine letzte Frage sollte noch geklärt werden: Um wieviele Spalten einrücken? Ich denke, dass 2 hier eine gute Zahl ist. Durch den hohen Grad der Verschachtelung von Lisp-Code halte ich größere Abstände für ungeeignet - würde man z.B. die in C üblichen vier Spalten verwenden, laufen die Zeilen sehr schnell rechts aus dem Editor-Fenster. Für eine Unsitte halte ich das Einrücken mit Tabulatoren - die gesamte Formatierung wird oft genug zerstört, wenn die Dateien auf anderen Rechnern, mit anderen Editoren usw. geöffnet werden - insbesondere dann, wenn Tabs und Leerstellen gemischt eingesetzt wurden.

Hier die Abbildung von einem Stück Code, in der deutlich gemacht wird, wie leicht man die Zusammengehörigkeit von Klammern sehen kann, wenn man sich an die Regel hält, dass eine Klammer entweder noch in der selben Zeile geschlossen wird, oder aber genau senkrecht unter der öffnenden:

Ein Beispiel für korrekt eingerückten Code


Nun zum zweiten Thema dieses Kapitels: Kommentare. Sie werden ja vom Interpreter ignoriert - warum sollte man also ein Programm kommentieren? Auch hier ist die Antwort (wie beim Einrücken) wieder: Wegen der Lesbarkeit! Warum ein Programm lesbar sein sollte, haben wir ja schon erörtert. Allerdings sollten Kommentare auch so beschaffen sein, dass sie wirklich einen Beitrag dazu leisten - das folgende Beispiel tut das nicht:
; i um 1 inkrementieren
(setq i(1+ i))
                  
Solche Kommentare sind schlicht und ergreifend Blödsinn, denn dass i hier inkrementiert wird, sieht man auch ohne den Kommentar. So etwas erhöht die Lesbarkeit nicht, es vermindert sie. Und was für Kommentare machen Sinn? Dazu wieder Beispiele:
; erstes Element überspringen, weil ...
(foreach item(cdr liste)
                  
So ein Kommentar kann sehr viel Nachdenken ersparen, weil man nicht erst herausfinden muss, WARUM das erste Element ausgelassen wird
; die Liste der Inserts sortieren und
; alle diejenigen entfernen, die ...
(mapcar
  (function
    (lambda(inserts / )
      (remove-if-not
        (vl-sort ....)
          ...
          ...
                  
Hier wird eine vielleicht nicht ganz einfache Stelle des Programms mit ein paar Worten beschrieben - eine Hilfe, um schnell zu erfassen, was da passiert. Grundsätzlich sollte auch jede Funktion als Ganzes kommentiert sein. So könnte das absolute Minimum aussehen:
;prüft, ob Entity eine Ellipse ist
(defun ellipse-p(ent / )
  (= "ELLIPSE"(cdr(assoc 0(entget ent))))
)

;prüft, ob Entity ein Kreis ist
(defun circle?(ent / )
  (= "CIRCLE"(cdr(assoc 0(entget ent))))
)
                  
Die Schreibweise mit '-p' am Ende des Namens entspricht den Lisp-Konventionen, wobei 'p' für 'predicate' steht. Prädikatfunktionen testen etwas und geben entweder T oder nil zurück. Daher braucht man, wenn man sich an solche Konventionen hält, auch nicht kommentieren, wie die Rückgabe der Funktion aussieht.

Die Schreibweise mit dem Fragezeichen ist an den Lisp-Dialekt Scheme angelehnt und nach meiner Ansicht noch intuitiver. Sie kennzeichnet aber genau den selben Sachverhalt und erspart das Kommentieren der Rückgabe. Funktionen, die ihres Seiteneffekts wegen geschrieben werden, kennzeichnet man dort übrigens mit einem Ausrufungszeichen am Ende. Ein Beispiel:
; Zweck:        Destruktive Form von append
; Effekt:       Gesamtliste
; Seiteneffekt: Bindung von symbol1 wird
;               geändert
(defun append!(symbol1 liste2 / )
  (set symbol1(append(eval symbol1)liste2))
)
                  
Näheres zu destruktiven Formen sind auf den Seiten für Fortgeschrittene im Kapitel Destruktiv zu finden. Eine vollständige Kommentierung einer Funktion könnte übrigens etwa so aussehen:
; ****************************************
; Funktion:     mapin
; Zweck:        wendet ein lambda-expression auf
;               eine verschachtelte Liste an
; Autor:        Axel Strube-Zettler
; Datum:        01.10.95
; Effekt:       modifizierte Liste
; Seiteneffekt: Keiner
; Known bugs:   Keine
; siehe auch:   nothing-happens
; Argumente:    expr - der Lambda-Ausdruck
;               a - die Liste
; Beispiel:     (mapin
;                '(lambda(elem)
;                   (if(='STR(type elem))
;                     (strcase elem)
;                     elem
;                   )
;                 )
;                '("a"(1 "b")("c" 2 ("d"(3 "e"))))
;               )
;               =>("A"(1 "B")("C" 2 ("D"(3 "E"))))
; Definition:

(defun mapin(expr a / )
  (cond
    ( (null a)nil)
    ( (atom a)((eval expr)a))
    ( (and(=(type a)'LIST)(cdr a)(atom(cdr a)))
      (cons
        (mapin expr(car a))
        ((eval expr)(cdr a))
      )
    )
    ('T
      (append
        (list(mapin expr(car a)))
        (mapin expr(cdr a))
      )
    )
  )
)

; ****************************************
                  
Näheres zur Funktion mapin sind auf den Seiten für Fortgeschrittene im Kapitel Tiefer rein! zu finden.

Wer alle seine Funktionen so auskommentiert, wird sicherlich keine Probleme haben, auch nach Jahren wieder nachzuvollziehen, was in einem Programm eigentlich passiert, und auch jemand anders wird keine Schwierigkeiten haben, sich in ein Programm einzuarbeiten. Der kommerzielle Wert von Sourcecode hängt übrigens weniger davon ab, ob er elegant, effektiv oder sonst etwas ist. Viel preisbestimmender ist die Qualität der Dokumentation, die herrschende Ordnung und die daraus resultierenden Einarbeitungszeiten!

Nur ganz kurz möchte ich hier noch auf Kommentar-Systeme verweisen: Es ist durchaus möglich, Sourcecode mit anderen Programmen zu bearbeiten. Bekannt sind in AutoLisp z.B. Kommentierungsregeln mit einem, zwei oder drei Semikola am Zeilenanfang, die dann von Skripten (auch in Lisp geschrieben, aber auch in perl usw.) in Dokumentations-Seiten (z.B. HTML oder Textdateien) umgesetzt werden. Ein Bibliotheks-Handbuch kann dann auf Knopfdruck erzeugt werden.

Und ein letztes Wort noch zum Thema 'pretty printer': Keine Drucker im Designer-Gehäuse, sondern Funktionen, die den AutoLisp-Code lesen und anschliessend sauber eingerückt wieder ausgeben. Der Editor der Visual-Lisp-IDE von AutoCAD hat eine solche 'pretty printing'-Funktion eingebaut. Aber man kann in AutoLisp selbst geschriebene pretty printer auch im Netz finden und herunterladen. Es ist nicht einmal soooo schwer, sich eine solche Funktion selbst zu schreiben!
Übungsaufgaben

  1. Sagen wir einfach mal: Hitzefrei!
    Zu diesem Kapitel gibt es keine Übungsaufgaben.