Immer wieder werden in Newsgroups und Diskussionen Fragen gestellt wie
z.B. 'Wie kann ich eine Funktion definieren, die eine unterschiedliche
Anzahl von Argumenten erhalten kann?'. Schade eigentlich, aber wir müssen
damit leben, dass in AutoLisp ein paar Dinge, die man in anderen Lisp-Dialekten
gewöhnt ist, einfach nicht implementiert sind. Hier sind drei Dinge
hervorzuheben, deren Fehlen zwar schmerzlich ist, die aber trotzdem mit
anderen Techniken emuliert werden können und daher in gewisser Weise
doch verzichtbar sind:
-
Es können keine Funktionen mit variabler Anzahl von Argumenten
definiert werden. Es gibt keine optionalen Argumente, keine
benannten Argumente, keine Vorgabewerte für Argumente usw.
-
Es gibt keine Makros in AutoLisp. Daher werden bei jeder selbstdefinierten
Funktion alle Argumente evaluiert.
-
Ebensowenig gibt es in AutoLisp einen Mechanismus für das Backquoting.
Wenn eine Liste gequotet ist, dann ist sie das ausnahmslos und
vollständig.
Wie aber können wir diese Techniken substituieren? Im ersten Fall ist es
recht einfach zu handhaben, und es stehen mehrere, allerdings ähnliche
Methoden zur Verfügung. Wenn ein einzelnes optionales Argument benötigt
wird, müssen wir ein zusätzliches Argument einführen, dass dann auch immer
angegeben werden muss. Der Optionscharakter wird dadurch emuliert, dass
entweder
nil oder etwas anderes übergeben wird. Weggelassen werden darf das
nil aber keinesfalls!
Mehrere Restargumente können als Liste übergeben werden. Auch in diesem
Fall muss aber ein
nil (eine leere Liste) übergeben werden, wenn gar
keine zusätzlichen Argumente bei einem Funktionsaufruf benötigt werden. Dazu
ein kleines Beispiel:
(defun my-func(a b c / d e)
(setq e(caddr c) d (cadr c) c (car c))
...
)
; so wird die Funktion aufgerufen
(my-func 10 11 '("a" "b" "c"))
Benannte Argumente machen dann Sinn, wenn aus einer Vielzahl von möglichen
Argumenten meistens nur wenige übergeben werden. Auch in diesem Fall
können wir die Argumente als Liste zusammenfassen, müssen aber auch wieder
ein
nil übergeben, wenn keine Zusatzargumente da sind. Im Unterschied zu
vorher werden die Argumente hier als Assoziationsliste übergeben. Sie
müssen daher auch nicht in einer festgelegten Reihenfolge angeführt werden.
(defun ....
)
Wenn es hier nicht um die Übergabe von Datenlisten gehen soll, sondern um
die (notgedrungene) Übergabe von Funktionsargumenten, sollten diese Argumente
dann innerhalb der Funktion auch als lokale Variablen vorhanden sein. Dies
wurde mit den
(setq ...) - Anweisungen im vorigen Beispiel bereits
gewährleistet. Wenn's mal wieder etwas länger wird, könnte man auch auf folgende
Idee kommen, die Variablen lokal einzuführen:
(defun foo(a b c / d e f g h)
(foreach arg '(d e f g h)
(set arg(car c))
(setq c(cdr c))
)
....
)
Das ist eine saubere Lösung, alle nicht übergebenen Argumente haben den Wert
nil. Unschön ist allerdings die Tatsache, dass man hier eine Liste,
nämlich die der lokalen Funktionsvariablen '(d e f g h), noch einmal abtippen
muss.
Bei der Übergabe einer Assoziationsliste könnte man auf eine ganz schlaue Idee
kommen:
(defun foo(a b c / d e f g h)
(foreach item c
(set(car c)(cdr c))
)
....
)
Das sieht auf den ersten Blick überzeugend aus, ist aber alles andere als das!
Es gibt zwar einen Schutz, dass nicht versehentlich Variablen aus dem übergeordneten
Namensraum verwendet werden, da schliesslich alle Symbole in der Funktion lokal
deklariert sind. Aber es gibt keinen Schutz dagegen, dass der Aufrufer hier
Argumente übergibt, die die Funktion gar nicht haben will. Da diese Symbole hier
nicht lokal sind, entstehen ungewollte globale Variablen!
(foo 3 4 '((d . 5)(e . 6)(x . 7)))
Bei diesem Aufruf von foo werden d und e korrekt behandelt, f, g und h haben innerhalb
von foo den Wert nil, was ebenso korrekt ist, aber es entsteht eine globale Variable
x mit dem Wert 7, die nirgendwo ausgewertet wird.
Eine jederzeit korrekte und effektive Lösung wird erst in Verbindung mit
(let ...) möglich, weitere Informationen in diesem Kapitel. Näheres zum
fehlenden Backquoting und zu Makros sind im Kapitel 'Grenzen II' zu finden.