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










Manchmal gibt es Situationen in Programmen, in denen man die Argumente für einen Funktionsaufruf bereits in einer Liste zusammengfasst hat. Ein Grund dafür könnte sein, dass ihr Programm in einen Eingabe- und einen Ausführungsteil geteilt ist. Nehmen wir an, der Ausführungsteil zeichnet irgendein Werkstück als Variantenkonstruktion und benötigt 25 verschiedene Parameter.

In diesem Fall ist es sinnvoll, den Eingabeteil völlig abzuspalten. Dieses Verfahren bietet den Vorteil, dass verschiedene Eingabemodule den Ausführungsteil des Programms nutzen können: Benutzereingabe über die Kommandozeile (lässt sich dann auch über Scripts ausführen), Benutzereingabe über Dialogboxen (das empfinden viele Leute als praktischer, lässt sich aber nicht von Script-Dateien ausführen) und Auslesen der Zeichnungsparameter aus einer Steuerdatei sind die drei häufigsten Arten.

Egal mit welcher der drei Möglichkeiten Sie zu den Eingaben gekommen sind, müssten Sie nun den Ausführungsteil, der 25 Argumente erwartet, etwa so aufrufen:
(zeichne-variante
  (nth 0 parameterliste)
  (nth 1 parameterliste)
  (nth 2 parameterliste)
  (nth 3 parameterliste)
  ...
  (nth 25 parameterliste)
)
                  
Dieser reichlich sinnlosen Vergeudung Ihrer Schaffenskraft können Sie mit (apply) entgegentreten:
(apply zeichne-variante parameterliste)
                  
tut es hier als Eingabe genausogut. (apply) erwartet als erstes Argument den Namen einer Funktion und als zweites Argument eine Liste, deren Inhalt als Argumente an die Funktion übergeben wird. Bei (apply) wird also im Gegensatz zu (mapcar) die Funktion immer nur einmal ausgeführt. Die Länge der Liste muss mit der Anzahl der erwarteten Argumente der Funktion übereinstimmen. Ein paar Beispiele:
(apply '+ '(10 14 3 41 65))
  =>  133

(apply '=> '(33 27 21 15 9 3))
  =>  T

(apply '=> '(33 21 27 15 9 3))
  =>  nil
                  
In unseren Beispielen arbeiten wir hier immer mit Listen, deren Inhalt bekannt ist. In der Praxis ist die Länge von Listen jedoch meist unbekannt. Da die Länge der Liste, die an (apply ...) übergeben wird, mit der Anzahl der Argumente der anzuwendenden Funktion übereinstimmen muss, ist es im Zusammenhang mit Listen unbekannter Länge eigentlich nur möglich, Funktionen mit einer variablen Anzahl von Argumenten zu übergeben. Da man diese nicht selbst definieren kann, bleiben in diesem Fall nur die eingebauten Funktionen wie (+ ...) usw.

Ein weiteres Beispiel: Sie haben wieder eine Punkteliste pl und möchten die arithmetischen Mittel aller X-Werte und aller Y-Werte berechnen. Das Ergebnis des Ausdrucks soll eine Liste mit den beiden Mittelwerten sein. Das könnte dann etwa so aussehen:
(list
  (/(apply '+ (mapcar'car pl))(length pl))
  (/(apply '+ (mapcar'cadr pl))(length pl))
)
                  
Und wenn's nun auch noch 3D sein soll? Es wäre natürlich einfach, auch noch einen dritten Ausdruck für die Z-Werte einzusetzen.
(list
  (/(apply '+ (mapcar'car pl))(length pl))
  (/(apply '+ (mapcar'cadr pl))(length pl))
  (/(apply '+ (mapcar'caddr pl))(length pl))
)
                  
Aber soll es denn wirklich in derartige Schreibarbeit ausarten? Wir turnen also doch lieber einen doppelten (mapcar) mit eingesprungenem (lambda):
(mapcar
 '(lambda(arg)
    (/(apply '+(mapcar arg pliste))(length pliste))
  )
 '(car cadr caddr)
)
                  
Lassen Sie sich Zeit, diese Konstruktion nachzuvollziehen. Sie ist ja auch wirklich nicht ganz einfach. Experimentieren Sie nach Möglichkeit eine Weile mit ähnlichen Ausdrücken und versuchen Sie ein wenig Sicherheit im Umgang mit solchen Ausdrücken zu entwickeln!

Und nun zur Funktion (foreach). Es handelt sich hier zwar auch um eine Funktion zur Bearbeitung von Listen, ihr Funktionscharakter gleicht aber mehr den Funktionen zur Schleifensteuerung. (foreach) erwartet mindestens drei Argumente: Zuerst einen frei wählbaren Symbolnamen, dann eine Liste, und schliesslich mindestens einen zu evaluierenden Ausdruck. Das kann im Beispiel so aussehen:
(foreach datei
 '("progr1" "progr3" "progr7" "progr9")
  (load datei)
)
                  
Dieser Ausdruck ist in der Wirkung identisch mit
(load "progr1")
(load "progr3")
(load "progr7")
(load "progr9")
                  
(foreach) ordnet nacheinander dem Symbol datei die Elemente der Liste zu und evaluiert dann jeweils den in Argument 3 gegebenen Ausdruck. Beachten Sie, dass das erste und das dritte Argument nicht quotiert wurden. (foreach) verhindert die Evaluation von Argument 1 und Argument 3 automatisch.

Die Rückgabe von (foreach) ist identisch mit der Rückgabe der letzten durchgeführten Evaluation von Argument 3. Daraus schliessen wir, dass auch bei (foreach) der Haupteffekt Nebensache und der Nebeneffekt die Hauptsache ist. In der Praxis wird der Haupteffekt von (foreach) eigentlich nie verwertet.

Wir werden gleich darauf kommen, warum man (foreach) auch als Schleifensteuerung betrachten kann. Zunächst soll erst einmal die Nähe zu (mapcar) untersucht werden:
(mapcar
 '(lambda(datei / )
    (load datei)
  )
 '("progr1" "progr3" "progr7" "progr9")
)
                  
Mit (mapcar) und (lambda) lässt sich also das Selbe bewerkstelligen, allerdings mit etwas mehr Aufwand. Der Unterschied: Die (mapcar)-Konstruktion gibt eine Liste mit den Ergebnissen aller (load)-Aufrufe zurück. Die Version mit (foreach) gibt nur das Ergebnis des letzten durchgeführten Aufrufs von (load ...) zurück.

Wir können das so zusammenfassen: Man sollte eine (mapcar)-Konstruktion verwenden, wenn man am Effekt interessiert ist. Ist nur der Neben- bzw. Seiteneffekt von Interesse, ist die Anwendung von (foreach ...) ein wenig einfacher.

(foreach) ist neben (lambda) die einzige in AutoLisp eingebaute Funktion, die für ein Symbol einen Namensraum erzeugt. Das bedeutet, dass das Symbol datei nur innerhalb der (foreach ...)-Anweisung definiert ist. Betrachten wir unser Beispiel noch einmal als (nicht korrekte) Funktionsdefinition:
(defun dateien-laden(dateiliste / datei)
  (foreach datei dateiliste
    (load datei)
  )
)
                  
Dieser Code sieht korrekt aus und funktioniert hunderprozentig, was soll also falsch daran sein? Wir haben hier zwei Variablen mit dem Namen datei erzeugt! Der Eintrag von datei als lokale Variable ist völlig überflüssig!

Ein weiteres Beispiel: Eine Liste enthält einen in Einzelteile zerlegten vollständigen Dateinamen. Es soll eine Funktion definiert werden, die solche zerlegten Dateinamen wieder zusammensetzt:
(defun name-zusammensetzen(teile / ganz)
  (setq ganzname "")
  (foreach teil
    (reverse(cdr(reverse teile)))
    (setq ganz(strcat ganz teil "\\"))
  )
  (setq ganz
    (strcat ganz "."(last teile))
  )
)

(setq name-zum-testen
 '("c:" "acad" "lisp" "progr" "letztes" "lsp"))

(name-zusammensetzen name-zum-testen)
    => "c:\acad\lisp\progr\letztes.lsp"
                  
Sollten Sie hier Schwierigkeiten mit dem Backslash haben, dann lesen Sie bitte noch einmal bei der Funktion (load) nach!

Wir greifen nun noch einmal eines unserer Beispiele aus einem vorigen Kapitel wieder auf. Es ging darum, in einer Liste mit Punkten doppelt vorkommende Koordinaten zu finden. Noch einmal der Stand der Dinge:
(defun doppelter-ywert(ywert punkte)
  (member ywert
    (cdr(member ywert(mapcar 'cadr punkte)))
  )
)
                  
Diese Funktion war immerhin schon in der Lage, einen zahlenmässig bestimmten y-Wert zu suchen und anzuzeigen, ober er mehr als einmal vorkommt. Erweitern wir die Funktion jetzt mit (foreach), sodass sie irgendeinen doppelten y-Wert findet:
(defun doppelter-ywert(liste / ywert ergebnis)
  (foreach ywert(mapcar 'cadr liste)
    (if
      (member ywert
        (cdr(member ywert(mapcar 'cadr liste)))
      )
      (setq ergebnis(append ergebnis ywert))
    )
  )
  ergebnis
)
                  
Die neue Version unserer Funktion stellt schon einen qualitativen Schritt nach vorn dar. Sie erfüllt Ihren Zweck, und sie gibt statt eines blossen Listenrestes eine Liste zurück, in der alle mehrfachen Vorkommen von Y-Werten erfasst sind. Auch diese Liste ist als logisches 'wahr' interpretierbar, wenn man an weiteren Informationen interessiert ist, kann man sie auswerten.

Aber noch einmal zurück zu (foreach) und seiner Nähe zur Schleifensteuerung. Es ist in der Tat nicht aufwändig, eine (foreach)-Anweisung durch eine (repeat)-Schleife zu ersetzen. Wir müssen nur etwas umstellen:
(foreach element liste ... )

; wird zu:

(setq i 0)
(repeat(length liste)
  (setq element(nth i liste)i(1+ i))
  ...
)
                  
Anstelle der Pünktchen kann man den zu evaluierenden Ausdruck (oder auch mehrere) einsetzen und wird mit beiden Konstruktionen zum gleichen Ergebnis kommen. (foreach) ist also eine Funktion, die zwar praktisch und übersichtlich anzuwenden ist, aber im Grunde - wie wir z.B. schon bei (setq) erörtert haben - nicht unbedingt notwendig ist.