Suche…


Wann ist eine Gruppierung erforderlich?

An einigen Stellen in Common Lisp werden eine Reihe von Formularen nacheinander ausgewertet. Zum Beispiel im Körper eines Defun oder Lambda oder im Körper eines Dotimes . In diesen Fällen funktioniert das Schreiben mehrerer Formulare wie erwartet. An einigen Stellen, wie dem Dann- und anderen Teil eines If- Ausdrucks, ist jedoch nur ein einziges Formular zulässig. Natürlich möchten Sie tatsächlich mehrere Ausdrücke an diesen Stellen auswerten. Für diese Situationen ist eine implizite Form der expliziten Gruppierungsform erforderlich.

Progn

Der allgemeine Zweck spezieller Operator progn zur Auswertung null oder mehr Formen verwendet. Der Wert des letzten Formulars wird zurückgegeben. Im Folgenden wird beispielsweise (print 'hello) ausgewertet (und das Ergebnis wird ignoriert), dann wird 42 ausgewertet und das Ergebnis ( 42 ) zurückgegeben:

(progn 
  (print 'hello)
  42)
;=> 42

Wenn sich keine Formulare im progn befinden , wird nil zurückgegeben:

(progn)
;=> NIL

Zusätzlich zum Gruppieren einer Reihe von Formularen hat progn auch die wichtige Eigenschaft, dass, wenn das Prognosformular ein Formular auf oberster Ebene ist , alle darin enthaltenen Formulare als Formular auf oberster Ebene verarbeitet werden. Dies kann wichtig sein, wenn Sie Makros schreiben, die sich zu mehreren Formularen erweitern, die alle als Formular auf oberster Ebene verarbeitet werden sollen.

Progn ist auch insofern wertvoll, als es alle Werte der letzten Form zurückgibt. Zum Beispiel,

(progn
  (print 'hello)
  (values 1 2 3))
;;=> 1, 2, 3

Im Gegensatz dazu geben einige Gruppierungsausdrücke nur den Primärwert des Ergebnisformulars zurück.

Implizite Prognosen

Einige Formulare verwenden implizite Prognosen , um ihr Verhalten zu beschreiben. Zum Beispiel die , wann und es sei denn , Makros, die im wesentlichen einseitig sind , wenn Formen, ihr Verhalten in Bezug auf eine implizite progn beschreiben. Das bedeutet, dass eine Form gefällt

(when (foo-p foo)
  form1
  form2)

ausgewertet wird und die Bedingung (foo-p foo) wahr ist, dann werden form1 und form2 so gruppiert, als wären sie in einem progn enthalten . Die Erweiterung des when -Makros ist im Wesentlichen:

(if (foo-p foo)
  (progn
    form1
    form2)
  nil)

Prog1 und Prog2

Oft ist es hilfreich, mehrere Ausdrücke auszuwerten und das Ergebnis aus dem ersten oder dem zweiten Formular anstelle des letzten zurückzugeben. Dies ist mit let und z. B. einfach zu bewerkstelligen:

(let ((form1-result form1))
  form2
  form3
  ;; ...
  form-n-1
  form-n
  form1-result)

Da diese Form in einigen Anwendungen üblich ist, enthält Common Lisp prog1 und prog2 , die prognos sind , aber das Ergebnis der ersten bzw. zweiten Form zurückgeben. Zum Beispiel:

(prog1
  42
  (print 'hello)
  (print 'goodbye))
;; => 42
(prog2
  (print 'hello)
  42
  (print 'goodbye))
;; => 42

Ein wichtiger Unterschied zwischen prog1 / prog2 und progn ist jedoch, dass progn alle Werte der letzten Form zurückgibt , wohingegen prog1 und prog2 nur den Primärwert der ersten und zweiten Form zurückgeben. Zum Beispiel:

(progn
  (print 'hello)
  (values 1 2 3))
;;=> 1, 2, 3

(prog1
  (values 1 2 3)
  (print 'hello))
;;=> 1              ; not 1, 2, 3

Verwenden Sie für mehrere Werte mit der Prog1 -Stilbewertung stattdessen Multiple-Value-Prog1 . Es gibt kein ähnliches Multiple-Value-Prog2 , aber es ist nicht schwer zu implementieren, wenn Sie es brauchen.

Block

Der spezielle Operator Block erlaubt das Gruppieren mehrerer Lisp Formen (wie ein implizites progn ) und es nimmt auch einen Namen , den Block zu nennen. Wenn die Formulare innerhalb des Blocks ausgewertet werden, kann der spezielle Operator return-from verwendet werden, um den Block zu verlassen. Zum Beispiel:

(block foo
  (print 'hello)     ; evaluated
  (return-from foo)
  (print 'goodbye))  ; not evaluated
;;=> NIL

return-from kann auch mit einem Rückgabewert versehen werden:

(block foo
  (print 'hello)     ; evaluated
  (return-from foo 42)
  (print 'goodbye))  ; not evaluated
;;=> 42

Benannte Blöcke sind nützlich, wenn ein Codeabschnitt einen aussagekräftigen Namen hat oder wenn Blöcke verschachtelt sind. In manchen Zusammenhängen ist nur die Fähigkeit wichtig, von einem Block frühzeitig zurückzukehren. In diesem Fall können Sie nil als Blocknamen verwenden und zurückkehren . Rückgabe ist wie Rückgabe , nur dass der Blockname immer Null ist .

Hinweis: Bei den beiliegenden Formularen handelt es sich nicht um Formulare der obersten Ebene. Das unterscheidet sich von progn , wo die eingeschlossenen Formen einer Top-Level- progn immer noch als Top-Level- Formulare betrachtet werden.

Tagbody

Für viel Kontrolle in Gruppenformularen kann der Tagbody- Spezialoperator sehr hilfreich sein. Die Formulare innerhalb eines Tagbody- Formulars sind entweder go-Tags (die nur Symbole oder ganze Zahlen sind) oder Formulare, die ausgeführt werden sollen. In einem Tagbody wird mit dem Operator go special die Ausführung an einen neuen Ort übertragen. Diese Art der Programmierung kann als ziemlich untergeordnet angesehen werden, da sie beliebige Ausführungspfade zulässt. Das folgende ist ein ausführliches Beispiel, wie eine for-Schleife aussehen könnte, wenn sie als Tagbody implementiert wird :

(let (x)               ; for (x = 0; x < 5; x++) { print(hello); }
  (tagbody
     (setq x 0)
   prologue
     (unless (< x 5)
       (go end))
   begin
     (print (list 'hello x))
   epilogue
     (incf x)
     (go prologue)
   end))

Tags und unterwegs werden zwar nicht häufig verwendet, möglicherweise aufgrund von "GOTO als schädlich", können jedoch bei der Implementierung komplexer Kontrollstrukturen wie Zustandsmaschinen hilfreich sein. Viele Iterationskonstrukte erweitern sich auch zu einem impliziten Tagbody . Beispielsweise wird der Hauptteil eines Punkts als eine Reihe von Tags und Formularen angegeben.

Welches Formular verwenden?

Beim Schreiben von Makros, die sich zu Formularen entwickeln, die eine Gruppierung beinhalten könnten, lohnt es sich, darüber nachzudenken, in welcher Gruppenkonstruktion die Erweiterung erfolgen soll.

Für Definitionsstil-Formulare, zum Beispiel ein Define-Widget- Makro, das normalerweise als Formular der obersten Ebene angezeigt wird und das mehrere Defun s, Defstruct s usw. enthält, ist es normalerweise sinnvoll, eine progn zu verwenden , sodass untergeordnete Formulare verwendet werden als Top-Level-Formulare verarbeitet. Bei Iterationsformen ist ein impliziter Tagbody häufiger.

Zum Beispiel kann der Körper von DOTIMES , Dolist und tun erweitert jeweils in eine implizite Tagbody.

Für Formulare, die einen benannten "Chunk" von Code definieren, ist ein impliziter Block oft nützlich. Während sich der Körper eines Defun innerhalb einer impliziten Prognose befindet , befindet sich diese implizite Prognose beispielsweise in einem Block, der den Namen der Funktion teilt. Das bedeutet, dass mit return- from die Funktion verlassen werden kann. Eine solche comp



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow