Hier geht es um eine weitere Funktion, die in Lisp-Dialekten eigentlich zum
Standard gehört, aber in AutoLisp nicht implementiert wurde:
(let ...)
schafft einen lokalen Namensraum für Variablen und bindet Werte an die
Symbole. Lokale Variablen, die hinter dem Schrägstrich in der Argumentenliste
deklariert werden, sind ja hinlänglich bekannt. Aber es gibt ja dann auch noch
Bindungen, deren Namensraum noch kleiner ist: Denken wir doch mal an
(foreach item ...)!
foreach schafft für das Symbol
item einen Namensraum, der auf die
foreach-Schleife beschränkt
ist. Es daher unnötig, das Symbol
item in der Argumentenliste der
umgebenden Funktion lokal zu deklarieren (tut man es trotzdem, hat man es
mit zwei verschiedenen Variablen zu tun, die zwar den gleichen Namen haben,
aber in verschiedenen Namensräumen existieren).
Solche verkleinerten Namensräume innerhalb von Funktionen entstehen aber
auch durch die Verwendung von
(lambda ...). Ganz streng genommen
entsteht allerdings wieder ein neuer Funktions-Namenraum, da
lambda
natürlich eine neue Funktion einlagert. Eine 'Verkleinerung' wie bei
foreach ist also eigentlich gar nicht gegeben. Genau diesen
lambda-Mechanismus nutzen wir nun, um unser
let zu definieren.
Der Code fällt recht kurz aus:
(defun let(bindings body / )
(eval
(cons
(append
(list'lambda(mapcar 'car bindings))
(list body)
)
(mapcar 'cadr bindings)
)
)
)
Zunächst einmal ein Anwendungsbeispiel, das gleichzeitig demonstriert, wie
schön die im Argument
bindings eigene, zu
let lokale Variablen
erzeugen, die die vorhandenen, zur Testfunktion lokalen Variablen zeitweise
überdecken:
(defun let-test( / localvar1 localvar2)
(setq localvar1 3 localvar2 "X")
(princ "localvar1 in let-test: ")
(princ localvar1)
(terpri)
(princ "localvar2 in let-test: ")
(princ localvar2)
(terpri)
(let '((localvar1 7)(localvar2 "Y"))
'(progn
(princ "localvar1 in let: ")
(princ localvar1)
(terpri)
(princ "localvar2 in let: ")
(princ localvar2)
(terpri)
)
)
(princ "localvar1 in let-test: ")
(princ localvar1)
(terpri)
(princ "localvar2 in let-test: ")
(princ localvar2)
(terpri)
)
Und nun zu den möglichen Anwendungen von
(let ...). Grundsätzlich können
wir mit
let erst einmal Variablen auf Ausschnitte von Funktion begrenzen,
z.B. bei Schleifen:
(let '((i 0)) '(while (/= i ...
Die hat den Vorteil, dass die Variable i wirklich nur innerhalb dieser Schleife
bekannt ist. Insbesondere, wenn mehrere solcher Schleifen innerhalb einer Funktion
vorkommen, kann man so sicherstellen, dass jede Schleife ihren eigenen Zähler
i bekommt, ungewollte Einflüsse werden recht einfach ausgeschlossen.
Interessanter wird die Sache aber dann, wenn die
bindings gar nicht
direkt angegeben werden, sondern an ganz anderer Stelle erzeugt werden. Eine
solche Verwendung von
let könnte z.B. so aussehen:
(let (dialog-starten)
'(if(not aborted)
(if checker_1
...
...
)
)
)
aborted und
checker_1 könnte hier zu
let lokale Variablen
sein, die irgendwo in den mit einem Dialog zusammenhängenden Funktionen zu einem
bindings-Paket zusammengeschnürt wurden. In diesem Paket sind die Dialog-Keys
(natürlich mit
(read ...) in Symbole umgewandelt) zusammen mit den aus
dem Dialog ausgelesen Werten verpackt. Jeder Key des DCL-Dialogs steht dann
innerhalb der let-Anweisung als lokale Variable zur Verfügung.