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










Wir sollten uns nun noch einmal den Testfunktionen zuwenden, die uns AutoLISP zur Verfügung stellt. Wir haben bereits einige Funktionen kennengelernt: (listp ...) und (atom) wurden in den ersten Kapiteln über Listen vorgestellt, wobei wir nie vergessen sollten, dass beide Funktionen bei der Anwendung auf nil ein T zurückgeben. Die Vergleichsfunktionen wie (> ...), (< ...) und (= ...) wurden bereits im ersten der Kapitel über Programmverzeigungen besprochen.

Wenn bisher von Datentypen die Rede war, dann wurde dieser Begriff verwendet, ohne genau zu klären, welche Datentypen es in AutoLISP eigentlich gibt. Dies soll nun nachgeholt werden. Bereits bekannt sind Integer- und Realzahlen sowie Zeichenketten, wenn auch mit Zeichenketten bisher noch nicht viel gemacht wurde. Diese Datentypen haben die Eigenschaft, dass sie zu sich selbst evaluieren, d.h. mit anderen Worten, wir können sie sowohl quoten als auch evaluieren, ohne dass sich irgend etwas ändert. Ferner kennen wir den Datentyp Liste, wobei es keinen Unterschied macht, ob es sich um Datenlisten oder Programmcode handelt.

Die eingebauten Funktionen von AutoLISP stellen einen weiteren Datentyp dar, und ab AutoCAD 2000 werden auch mit (defun ...) erzeugte Funktionen diesem Datentyp zugeschlagen. In beiden Fällen sind es keine Listen, und wir haben auch keinen Zugriff auf ihren Programmcode. Ein weiterer uns bekannter Datentyp sind die Symbole.

Die Funktion (type ...) ermittelt den Datentyp eines Ausdrucks. (type) führt keine automatische Quotierung des Ausdrucks durch, da sonst die Antwort verfälscht würde. (type ...) gibt als Antwort eines der folgenden Symbole zurück (die Liste ist nicht ganz vollständig):
INT      - Integerzahl
REAL     - Realzahl
STR      - Zeichenkette
LIST     - Liste
SYM      - Symbol
SUBR     - Eingebaute Funktion
USUBR    - Benutzer-Funktion (seit AutoCAD 2000)
FILE     - Datei-Handle
EXSUBR   - Über die ADS def. Funktion
EXRXSUBR - Über RX def. Funktion
PICKSET  - Auswahlsatz
ENAME    - Entity-Name
nil      - Leere Liste oder nil-Symbol
                  
Wir wollen uns hier zunächst auf die wichtigsten Möglichkeiten beschränken und sie anhand einiger Beispiele erläutern (Ihre selbstdefinierte Funktion (sqr) ist noch geladen, wenn nicht, bitte noch einmal schnell (defun sqr(zahl)(* zahl zahl)) eingeben:
(type 0)                   =>  INT
(type 0.0)                 =>  INT
(type 1.93)                =>  REAL
(type pi)                  =>  REAL
(type 'pi)                 =>  SYM
(type type)                =>  SUBR
(type 'type)               =>  SYM
(type "Zeichenkette")      =>  STR
(type (* 3 4.5))           =>  REAL
(type '(* 3 4.5))          =>  LIST
(type unbekanntes-symbol)  =>  nil
(type 'unbekanntes-symbol) =>  SYM
    ;auch ein Symbol ohne Bindung ist ein Symbol
(type 'a)                  =>  SYM
(type ''a)                 =>  LIST
    ;= (QUOTE(QUOTE A)), nur 1 Stufe wird evaluiert
(type sqr)                 =>  LIST
    ; Bis AutoCAD 14
(type sqr)                 =>  USUBR
    ; Ab AutoCAD 2000. Wenn die Funktion in der
    ; Kommandozeile eingegeben wurde, kann statt
    ; dessen auch SUBR erscheinen!
(type nil)                 =>  nil
(type '())                 =>  nil
                  
Die Bedeutung der anderen Rückgabesymbole wird noch in den nächsten Kapiteln behandelt. Sie kann aber auch in der AutoCAD-Hilfe nachgeschlagen werden.

Mit der (type)-Funktion können wir viele Auswahl- oder Entscheidungsprozesse steuern. Zwei der wesentlichen Aufgaben von (type) sind, sich Informationen über den Inhalt von Listen oder den Typ von übergebenen Argumenten zu beschaffen. Wenn Sie eine eingebaute Lisp-Funktion mit falschem Argument aufrufen, erhalten Sie sofort eine entsprechende Fehlermeldung. Bei Ihren selbstgeschriebenen Funktionen findet eine solche Prüfung nicht statt, denn Sie legen in der formalen Argumenteliste ja nur Anzahl und funktionsinterne Namen der Argumente fest, nicht aber deren Datentyp.

Lisp hindert Sie also nicht daran, z.B. (sqr "Käsebrot") einzugeben. Die Funktion (sqr) hat dummerweise keinerlei Ahnung, was sie da an (* ...) weiterleitet. Erst der Multiplikation wird das Käsebrot im Halse steckenbleiben.

Normalerweise können Sie auf eine selbstgestrickte Typprüfung verzichten. Wenn Ihre Programme halbwegs durchdacht sind, dann können Sie darauf vertrauen, dass (sqr) immer nur mit Zahlen aufgerufen wird. Sie wissen ja, welche Variablen Zahlen enthalten und werden (sqr) nur auf diese Variablen anwenden. AutoLISP bietet auch eine ganze Reihe von Benutzereingabe-Funktionen, die schon im Vorfeld verhindern, dass der Benutzer statt der erwarteten Zahl "Käsebrot" eingibt.

Es treten aber auch immer wieder Situationen auf, in denen Sie nicht mehr genau wissen, was eine Funktion oder ein Ausdruck da zur Verarbeitung erhalten wird. Der einfachste Fall ist, dass eine Liste, die zur Verarbeitung ansteht, möglicherweise leer sein könnte. Das liesse sich so formulieren:
(if (= (type unbekannte-liste) nil)(...))
                  
Kürzer wäre hier allerdings die Verwendung von (not) bzw. (null):
(if (not unbekannte-liste)(...))
(if (null unbekannte-liste)(...))
                  
Die Funktionen (not) und (null) sind in der Wirkung gleich, beide geben dann T zurück, wenn die Evaluation des Argumentes nil ergibt. Aus traditionellen Gründen wird meistens (null) für Listen angewendet und (not) bei anderen Objekten und in logischen Verknüpfungen.

Beim Umgang mit den Testfunktionen müssen Sie sich mit feinen Unterschieden befassen. Brauchen Sie einen Test, der nur prüft, ob etwas eine Liste ist, dann verwenden Sie (listp). Dieser Test wird auch leere Listen, also nil, durchgehen lassen. Benötigen Sie jedoch einen Test, dann ist die Formulierung
(if (= (type unbekannte-liste) 'list) (...))
                  
angebracht. Sie sollten hier übrigens das Quote-Zeichen vor dem Symbol list beachten. Es muss sein, da Sie ja die Rückgabe mit dem Symbol list vergleichen wollen, nicht mit einem darangebundenen Wert (das wäre der Funktionsinhalt der Funktion (list)).

Hier noch einige Bespiele, wie man Verzweigungen und Schleifen in einem Programm von Testfunktionen abhängig machen kann:
(setq zahl 100)
(while (not (zerop zahl))
  ...
  (setq zahl(1- zahl))
)
; Diese Schleife wird 100 mal durchlaufen.
; Dabei wird die Zahl 100 mal heruntergez„hlt.
; Sie hat also nach dem Ende der Schleife
; den Wert 0!
                  
Im nächsten Beispiel verwenden wir (minusp). Diese Funktion testet, ob eine Zahl negativ, d.h. kleiner als 0 ist. Ausserdem wird hier die ebenfalls neue Funktion (1- zahl) verwendet: Hier geht es um das Dekrementieren einer Zahl, es wird also 1 von zahl abgezogen. (1- zahl) ist also im Prinzip das Gleiche wie (- zahl 1). Die Gegenteils-Funktion heisst natürlich (1+ zahl) und inkrementiert die Zahl.

Dass es diese Funktionen gibt, ist eher historisch zu sehen. In der Zeit, als Lisp entwickelt wurde, sorgten diese beiden Funktionen dafür, dass statt einer wirklichen Subtraktion bzw. Addition statt dessen die Inkrement-/Dekrement-Funktion der Maschinensprache des Rechners ausgeführt wurde, um Rechenzeit zu sparen. Heutzutage spielen solche Überlegungen keine Rolle mehr, da aber die beiden Funktionen in vielen Lisp-Programmen verwendet werden, möchte ich sie hier vorstellen.
(setq laenge (1-(length zahlenliste))
(setq liste-ist-ok nil)
(while (not (minusp laenge))
  (if
    (or
      (/= (type (nth laenge zahlenliste))'real)
      (/= (type (nth laenge zahlenliste))'int)
    )
    (progn
      (setq liste-ist-ok nil)
      (setq laenge -1)
    )
    (progn
      (setq liste-ist-ok 'T)
      (setq laenge(1- laenge))
    )
  )
)
                  
Dieses Beispiel testet, ob in der Zahlenliste wirklich nur Integer- und Realzahlen vorhanden sind. Eine leere Liste soll von diesem Test abgelehnt werden. Etwas störend ist nur die Umständlichkeit der Prüfung. Daher eine zweite Version, die den Code etwas strafft:
(setq laenge (1- (length zahlenliste))
(setq liste-ist-ok nil)
(while (not (minusp laenge))
  (if
    (not(member(type(nth laenge zahlenliste))'(INT REAL)))
    (setq laenge -1 liste-ist-ok nil)
    (setq laenge (1- laenge) liste-ist-ok 'T)
  )
)
                  
Wir sollten uns über diese beiden Programmfragmente noch ein paar Gedanken machen. Warum ist es hier nötig, die Ergebnisvariable liste-ist-ok zuerst auf nil zu setzen? Antwort: Es könnte ja sein, dass die Liste leer ist. Dann ist nämlich laenge von vornherein -1 und die (while)-Schleife wird überhaupt nicht durchlaufen.

Wenn wir mit (defun) aus diesem Testausdruck eine Funktion machen würden, dann könnten wir uns diese Zeile sparen und liste-ist-ok lokal anlegen. Der Einsatz von (member) erspart uns, die (type)-Funktion zweimal aufzurufen und die Aufrufe mit (or) zu verknüpfen. Als kleinen Schönheitsfehler könnte man noch bezeichnen, dass liste-ist-ok jedesmal auf T gesetzt wird, wenn das Element eine Zahl ist. Vielleicht überlegen Sie sich ja eine bessere Lösung? Die (while)-Schleife wird übrigens abgebrochen, wenn ein Element gefunden wird, das keine Zahl ist. Das erreichen wir dadurch, dass wir laenge auf -1 setzen.


Übungsaufgaben

  1. Fassen Sie diesen Ausdruck ein wenig zusammen, so dass er nur noch eine Zeile hat und das or überflüssig wird:
    (or
      (/= (type (nth laenge zahlenliste))'real)
      (/= (type (nth laenge zahlenliste))'int)
    )