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!

Funktionen für komfortables Arbeiten mit Zeichenketten String-Tango
Noch mehr Funktionen für komfortableres Arbeiten mit Zeichenketten Kettenhunde
strtok zerlegt Zeichenketten anhand eines Trennzeichens Tock-Tock
Arbeiten mit Datum und Zeit in AutoLisp Zeitlos...
Dotted pairs - wie man den Programmabbruch verhindert Gepunktet?
Neue Funktionen für die Listenbearbeitung Strukturtapete
Weitere neue Funktionen für die Listenbearbeitung Listen to me!
Lambda expressions - dasSalz in der Suppe Lambada
Lambda expressions anhand eines Praxisbeispiels Unter der Erde
Where und whereever erleichtern den Umgang mit Listen Quo vadis?
Rekursion - Funktionen, die sich selbst aufrufen Katzenschwanz
Ein äusserst wichtiger Prototyp für Funktionen Nix passiert
Wo das lineare mapcar am Ende ist Tiefer rein!
Über Effekte und Neben(Seiten-)Effekte von Funktionen Seitensprünge
Die Namensräume (Sichtbarkeit) von Variablen Raumwunder
Let dient zur Schaffung kleinerer Namensräume Lass mal...
Sukzessive Verarbeitung von Listenresten mit mapcdr Der Bruder
Was in AutoLisp einfach nicht machbar ist (Teil I) Beschränkt
Was in AutoLisp einfach nicht machbar ist (Teil II) Limited Edition
Nicht mit Effekten arbeiten, sondern direkter Daten-Änderung Destruktiv
Sequenzielles vs. paralleles Abarbeiten von Argumenten Parallelwelten
Über den Umgang mit Funktionsschablonen Erwachet!
Ein Praxiskapitel über Auswahlsätze, Attribute, wcmatch und mehr Durch die Brust
Die Farben des AutoCAD Color Index und ihre RGB-Werte Alles so schön
Hier laufen die Fäden zusammen: Viele Konzepte vereint Lapsus Lispuli
Ein Spiel als Beispiel für lernfähige Funktionen Zug um Zug
Errorhandling in AutoLisp - Teil 1 Alles valsch!
Errorhandling in AutoLisp - Teil 2 Und foll Feler!


Zum Einsteiger-Tutorial

Zu den ActiveX-Seiten

Meine Private HP mit Fotos, Gedichten, Musik und Postkartenversand

Mein Online-Lexikon der Fotografie

Mein völlig abgedrehtes Reisebüro










Eines der immer wiederkehrenden Themen in AutoLisp ist die Behandlung von Block-Attributen. Deshalb möchte ich den Umgang damit hier als Beispiel für die Verwendung von lambda-expressions vorstellen. Die Werte von Attributen werden immer als Zeichenketten abgelegt, mit einem anderen Datentyp haben wir es nicht zu tun.

Zu allererst definieren wir uns eine Funktion, die uns das ständig wiederkehrende Schreiben von Schleifen bei der Arbeit mit Auswahlsätzen abnimmt:
(defun ss-foreach(ss expr / i)
  (if ss
    (repeat(setq i(sslength ss))
      ((eval expr)(ssname ss(setq i(1- i))))
    )
  )
)
                  
Auch (ss-foreach ...) erwartet neben dem Auswahlsatz als zweites Argument eine lambda-Funktion. Diese wiederum bekommt als Argument das jeweilige Element aus dem Auswahlsatz. Um die Zusammenhänge zu verdeutlichen, hier ein Beispiel:
(ss-foreach (ssget "X" '((0 . "insert")))
'(lambda (ins / )
    (print(cdr(assoc 2(entget ins))))
    (print"\n")
  )
)
                  
In diesem Beispiel wird nur der Block-Name eines jeden INSERTs in der Zeichnung ausgegeben. Wer das Kapitel über lambda-expressions aufmerksam gelesen hat, dem wird auf Anhieb klar sein, wie diese Funktion arbeitet. Der Variablenname ins ist hier natürlich völlig frei wählbar! Zu beachten ist aber, dass die übergebene Funktion immer genau ein Argument haben muss (für den Entity-Namen des jeweiligen INSERTs).

Als nächstes schreiben wir eine Funktion, die zu einem INSERT eine Liste mit den Entity-Daten aller daran hängenden Attribute erzeugt. Konstante Attribute werden dabei jedoch nicht berücksichtigt, da die Daten konstanter Attribute ja bekanntlich nicht am INSERT (das wären redundante Daten), sondern mit der Blockdefinition gespeichert werden.
(defun insert-getattdata(ent / retl)
  (setq ent(entget(entnext(cdr(assoc -1(entget ent))))))
  (while(= "ATTRIB"(cdr(assoc 0 ent)))
    (setq retl(cons ent retl))
    (setq ent(entget(entnext(cdr(assoc -1 ent)))))
  )
  (reverse retl)
)
                  
Die Daten werden in folgender Form zurückgegeben:
(
  (1- . "<....>")(0 . "ATTRIB")(10 . ...)...)
  (1- . "<....>")(0 . "ATTRIB")(10 . ...)...)
  (1- . "<....>")(0 . "ATTRIB")(10 . ...)...)
  ...
)
                  
Es handelt sich also um die vollständigen Elementdaten der Attribute, wie sie von (entget ...) ermittelt werden.

Als nächstes benötigen wir eine Funktion, die in der Lage ist, die gefundenen Attribute und ihre gespeicherten Werte auf irgendetwas hin zu testen. Irgendetwas? Genau, was da im Einzelnen zu testen ist, überlassen wir der späteren Anwendung. Wir stellen nur eine Rahmenfunktion zur Verfügung, die einen solchen Test durchführt.

Der Test selber wird - wie sollte es anders sein - natürlich als lambda-expression an die Funktion übergeben. Da die Funktion die Attributnamen mit (wcmatch ...) vergleicht, ist hier schon eine ganze Menge Möglichkeiten gegeben. Mit der lambda-expr zusammen steigern sich die Möglichkeiten sozusagen ins Unermessliche.
(defun test-attributes(insert namepat l-expr /
                                   atts result)
  (setq atts(insert-getattdata insert))
  (while atts
    (if
      (and
        (wcmatch
          (strcase(cdr(assoc 2(car atts))))
          (strcase namepat)
        )
        (apply l-expr(list(cdr(assoc 1(car atts)))))
      )
      (progn
        (setq result(car atts))
        (setq atts nil)
      )
      (setq atts(cdr atts))
    )
  )
  result
)
                  
Die Funktion gibt die Liste mit den Elementdaten des ersten Attributs zurück, bei dem die lambda-expression wahr wird. Falls dies bei keinem der Attribute eintritt, wird natürlich nil zurückgegeben. Die Prüfung wird abgebrochen, sobald ein entsprechendes Attribut gefunden wurde, sollten also mehrere Attribute der Bedingung entsprechen, wird nur eines gefunden.

Nun kommen wir zum Zusammenfügen der Teile: Die nächste Funktion dient zur recht allgemeinen Auswahl von Inserts mit Attributen in einer Zeichnung. Es wird eine Liste von Entitynamen zückgegeben, die alle Blöcke enthält, bei denen die Evaluation der übergebenen lambda-expression wahr wurde.
(defun sel-inserts
  (blnamepat attnamepat lambda-expr / tmp retlist)
  (ss-foreach
    (ssget "X"
      (list
      '(0 . "insert")
        (cons 2 blnamepat)
      )
    )
  '(lambda(ins / )
      (if(test-attributes ins attnamepat lambda-expr)
        (setq retlist(append retlist(list ins)))
      )
    )
  )
  retlist
)
                  
Die hohe Flexibilität der Funktion wird dadurch erreicht, dass
  • der verwendete ssget-Aufruf wcmatch verwendet (das ist bei ssget einfach so)
  • der Attribut-Name ebenfalls durch wcmatch verglichen wird (dafür haben wir in test-attributes gesorgt)
  • und wir das Ganze dann noch mit einem weiteren wcmatch im zu übergebenden lambda-Ausdruck aufrufen können
Nachfolgend einige Beispiele, wie flexibel (sel-inserts ...) verwendet werden kann:
; Alle Blöcke namens B1, bei denen das
; Attribut Laenge einen Wert von mehr
; als 1000 hat
(sel-inserts
  "B1"
  "Laenge"
 '(lambda(laenge / )(> (atof laenge) 1000))
)

; Alle Blöcke namens B1, bei denen eines der
; drei angegebenen Attribute einen Wert von mehr als
; 1000 hat
(sel-inserts "B1" "Laenge,Breite,Hoehe"
'(lambda(mass / )(> (atof mass) 1000))
)

; Alle Blöcke in der Zeichnung, die ein Attribut
; Laenge haben
(sel-inserts "*" "Laenge" '(lambda(ist-egal / )'T))

; Alle Blöcke in der Zeichnung, die überhaupt ein
; Attribut haben
(sel-inserts "*" "*" 'and)

; Alle Blöcke in der Zeichnung, deren Name mit einer
; Ziffer endet und die ein Attribut haben, dessen
; Name mit einem Buchstaben anfängt und in dessen Wert
; ein Umlaut vorkommt
(sel-inserts
  "*#"
  "@*"
 '(lambda(wert / )(wcmatch wert "*[äöüÄÖÜ]*"))
)
                  
Diese Beispiele sollten genügen, um zu zeigen, wie man mit diesen vier Funktionen auf Blöcke bzw. deren Attribute zugreifen kann. Zu beachten sind die beiden Beispiele, bei denen der übergebene lambda-Ausdruck immer T zurückgibt. Im ersten Falle ist es offensichtlich, da es explizit ausgeschrieben wurde.

Der zweite Fall, bei dem (and ...) verwendet wird, mag vielleicht auf Anhieb nicht so einsichtig sein. Machen wir uns aber klar, dass ein Aufruf von and mit einem einzigen Argument immer T zurückgibt, wenn das Argument nicht nil ist, dann wird klar, dass wir hier mit weniger Schreibaufwand genau zum gleichen Ergebnis kommen wie beim expliziten Ausschreiben der lambda-expression. Und wenn wir noch etwas weniger Aufwand beim Schreiben haben möchten, dann tut's or natürlich ganz genauso!