Paket »essentials« für Common Lisp

Das Paket essentials für Common Lisp stellt 4 Makros bereit, deren Verwendung die Formatierung und Lesbarkeit von Common-Lisp-Quelltext unterstützt.



Makro all-values

Das Makro all-values sammelt alle Rückgabe-Werte aller unmittelbar enthaltenen Formen und gibt sie als mehrfache Rückgabe-Werte in der Reihenfolge ihres Anfalls zurück.

Beispiel

(all-values (floor 9 6) (floor 7 3))

gibt vier Werte zurück: 1, 3, 2, 1.

Hinweis

In Common-Lisp-Implementierungen, die mehrfache Rückgabe-Werte nicht auf dem Stack übergeben, kann deren Verwendung negativ auf die Performanz wirken. Ein Beispiel dafür ist CLisp, dass mehrfache Rückgabe-Werte nicht auf dem Stack, sondern als Listen speichert.

Makro mlet, mlet*

Siehe auch

Die beiden Makros mlet und mlet* verschmelzen die Syntax von let und multiple-value-bind einerseits und von let* und multiple-value-bind andererseits.

Die Struktur einer mlet-Form ist der Struktur einer let-Form ähnlich. Insbesondere kann jede gültige let-Form in eine gültige mlet-Form dadurch umgewandelt werden, dass das Symbol let durch das Symbol mlet ausgetauscht wird.

Anders als let kann mlet mehrfache Rückgabe-Werte binden und dadurch die Verwendung von multiple-value-bind in Programmcode ersetzen.

Zusätzlich benötigte Symbole für weitere Rückgabe-Werte werden in den einzelnen Bindungs-Klauseln zwischen dem Symbol für den ersten Rückgabe-Wert und der Initialisierungs-Form eingefügt.

mlet führt wie let die Initialisierung der Bindungen parallel durch; mlet* führt sie wie let* in der Reihenfolge des Programmtextes durch. In beiden Fällen werden die Bindungen mehrfacher Rückgabe-Werte aus einzelnen Initialisierungs-Formen wie in multiple-value-bind immer parallel ausgeführt.

Beispiel 1

(let ((x (random (expt 10 10))))
  (multiple-value-bind
   (quotient remainder)
   (floor x)

   (format t "X: ~D · Quotient: ~D · Remainder: ~D"
           x quotient remainder)))

kann ersetzt werden durch

(mlet* ((x (random (expt 10 10))))
       ((quotient remainder (floor x)))

       (format t "X: ~D · Quotient: ~D · Remainder: ~D"
               x quotient remainder)))

Wie die Klauseln des iterate-Makros können die Bindungs-Klauseln von mlet und mlet* Typdeklarationen aufnehmen (im folgenden Beispiel in grüner Schrift).

Beispiel 2

(mlet* (((the bignum x) 
         (random (expt 10 10)))) 
       (((the unsigned-byte quotient)
         (the unsigned-byte remainder)
         (floor x)))
       (declare (optimize (speed 2)))

       (format t "X: ~D · Quotient: ~D · Remainder: ~D" 
               x quotient remainder)))

Die Typdeklarationen werden bei der Expansion von mlet und mlet* so platziert, dass ihr Gültigkeitsbereich sich mit dem Gültigkeitsbereich der jeweiligen Bindungen deckt. Im voranstehenden Beispiel umgibt der Gültigkeitsbereich der Typdeklaration bignum also den Gültigkeitsbereich der unsigned-byte-Typdeklarationen. Wie bei let und let* können nach den Bindungs-Klauseln weitere Deklarationen stehen.

Makro if?

Siehe auch

Das Makro if? ist dem Makro if* von John K. Foderaro ähnlich. Wie dieses stellt if? eine Variante von if (bzw. cond) mit Schlüsselwörtern bereit, um die Lesbarkeit des Programmtextes zu verbessern. Anders als bei if* sind die Zweige der if?-Form jedoch von runden Klammern umschlossen. Dies unterstützt die erwartungskonforme Einrückung in Texteditoren.

Beispiel 1

Die folgenden Formen sind alle äquivalent:

;; Klassisches 1950er Lisp-Konditional <cond>
(cond (testform-1)
      (testform-2
       alpha-1
       alpha-2
       alpha-3)
      (t
       beta-1
       beta-2))

;; Die <if>-Spezialform von Common Lisp
(let ((testvalue-10 testform-1)) 
  (if testvalue-10 
      testvalue-10 
    (if testform-2 
        (progn alpha-1 
               alpha-2 
               alpha-3) 
      (progn beta-1 
             beta-2))))

;; Das <if*>-Makro von John K. Foderaro
(if* testform-1
     :thenret
     :elseif testform-2
     :then alpha-1
     alpha-2
     alpha-3
     :else beta-1
     beta-2)

;; Typische Einrückung des <if?>-Makros
(if? testform-1
     (:thenret)
     (:elseif testform-2)
     (:then alpha-1
            alpha-2
            alpha-3)
     (:else beta-1
            beta-2))

Schon dieses einfach Beispiel zeigt, dass die Nutzung der if-Spezialform des Common-Lisp-Standards zu einer absurd unzugänglichen Einrückung führt.

Im if?-Makro können die Rückgabewerte der Testformen an Variablen-Namen gebunden werden. Ihre Bindung umschließt alle folgenden Klauseln der jeweiligen if?-Form. Dies gilt auch für mehrfache Rückgabe-Werte einer Testform. Angelehnt an die Syntax des iterate-Makros wird dafür das Schlüsselwort values verwandt (im folgenden Beispiel in blauer Schrift). Nur der jeweils erste Rückgabe-Wert einer :if– oder :elseif-Klausel wird getestet, um den Kontrollfluß zu steuern.

Beispiel 2

(if? (values testvalue-10 testform-1)
     (:thenret)
     (:elseif (values testvalue-20 
                      (ignore testvalue-21)
                      (the ratio testvalue-22)
                      testform-2)
     (:then alpha-1
            alpha-2
            ;; Hier sind die beiden format-Argumente an Rückgabe-
            ;;  Werte der beiden voranstehenden Tests gebunden:
            (format t "~S and ~S" testvalue-10 testvalue-22)
            alpha-3
     (:else beta-1
            beta-2))

Der Kontrollfluss wird hier von den Variablen testvalue-10 und testvalue-20 gesteuert. Typdeklarationen können wie in den Makros mlet und mlet* angegeben werden (hier in grüner Schrift); zusätzlich sind ignore– und ignorable-Deklarationen mit der von Common Lisp gewohnten Bedeutung möglich. Andere Deklarationen werden in Version 0.0.1 nicht erkannt.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.