Zoeken…


Wanneer is groepering nodig?

Op sommige plaatsen in Common Lisp wordt een reeks formulieren op volgorde geëvalueerd. Bijvoorbeeld in het lichaam van een defun of lambda , of in het lichaam van een stipimes . In die gevallen werkt het op volgorde schrijven van meerdere formulieren zoals verwacht. Op enkele plaatsen, zoals de toen en anders delen van een if- uitdrukkingen, is echter slechts een enkele vorm toegestaan. Natuurlijk wilt u misschien meerdere uitdrukkingen op die plaatsen evalueren. Voor die situaties is een soort impliciete of expliciete groepsvorm nodig.

Progn

De speciale operator prognose voor algemene doeleinden wordt gebruikt voor het evalueren van nul of meer formulieren. De waarde van het laatste formulier wordt geretourneerd. In het volgende wordt bijvoorbeeld (print 'hallo) geëvalueerd (en het resultaat ervan wordt genegeerd), waarna 42 wordt geëvalueerd en het resultaat ( 42 ) wordt geretourneerd:

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

Als er geen formulieren in de prognose staan , wordt nul geretourneerd:

(progn)
;=> NIL

Naast het groeperen van een reeks formulieren, heeft progn ook de belangrijke eigenschap dat als het progn- formulier een formulier op het hoogste niveau is , alle formulieren daarin worden verwerkt als formulieren op het hoogste niveau. Dit kan belangrijk zijn bij het schrijven van macro's die worden uitgebreid naar meerdere formulieren die allemaal als formulieren op het hoogste niveau moeten worden verwerkt.

Progn is ook waardevol omdat het alle waarden van de laatste vorm retourneert. Bijvoorbeeld,

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

Sommige groepsexpressies retourneren daarentegen alleen de primaire waarde van de resultaatvormende vorm.

Impliciete Progns

Sommige vormen gebruiken impliciete prognoses om hun gedrag te beschrijven. Bijvoorbeeld, de wanneer en tenzij macro's, die in wezen eenzijdige if- vormen zijn, beschrijven hun gedrag in termen van een impliciete voorspelling . Dit betekent dat een vorm zoals

(when (foo-p foo)
  form1
  form2)

geëvalueerd en de toestand (foo-foo p) waar is, en de form1 form2 gegroepeerd alsof ze zich in een progn. De uitbreiding van de When macro is in wezen:

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

Prog1 en Prog2

Vaak is het handig om meerdere expressies te evalueren en het resultaat uit de eerste of tweede vorm te retourneren in plaats van de laatste. Dit is eenvoudig te bereiken met let en bijvoorbeeld:

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

Omdat deze vorm gebruikelijk is in sommige toepassingen, bevat Common Lisp prog1 en prog2 die op progn lijken , maar respectievelijk het resultaat van de eerste en tweede vorm teruggeven. Bijvoorbeeld:

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

Een belangrijk onderscheid tussen prog1 / prog2 en progn is echter dat progn alle waarden van de laatste vorm retourneert , terwijl prog1 en prog2 alleen de primaire waarde van de eerste en tweede vorm retourneren. Bijvoorbeeld:

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

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

Voor meerdere waarden met prog1 -stijlevaluatie, gebruikt u in plaats daarvan multiple-value-prog1 . Er is geen vergelijkbare multiple-value-prog2 , maar het is niet moeilijk om te implementeren als je het nodig hebt.

Blok

De speciale operator blok maakt groepering van verschillende Lisp vormen (als een impliciete progn ) en neemt ook een naam aan het blok te noemen. Wanneer de formulieren in het blok worden geëvalueerd, kan de speciale operator return-from worden gebruikt om het blok te verlaten. Bijvoorbeeld:

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

Return-from kan ook worden voorzien van een retourwaarde:

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

Benoemde blokken zijn handig wanneer een stuk code een betekenisvolle naam heeft of wanneer blokken zijn genest. In sommige context is alleen de mogelijkheid om vroeg uit een blok terug te keren belangrijk. In dat geval kunt u nul gebruiken als de bloknaam en terugkeren . Return is net als return-from , behalve dat de bloknaam altijd nul is .

Opmerking: ingesloten formulieren zijn geen formulieren op het hoogste niveau. Dat is anders dan progn , indien de ingesloten vormen van een top-level progn vorm worden nog steeds beschouwd als het hoogste niveau vormen.

Tagbody

Voor veel controle in groepsvormen kan de tagbody speciale operator erg nuttig zijn. De formulieren in een tagbody- formulier zijn go-tags (dit zijn alleen symbolen of gehele getallen) of uit te voeren formulieren. Binnen een tagbody wordt de go special-operator gebruikt om de uitvoering over te dragen naar een nieuwe locatie. Dit type programmering kan als redelijk laag worden beschouwd, omdat het willekeurige uitvoeringspaden mogelijk maakt. Het volgende is een uitgebreid voorbeeld van hoe een for-loop eruit zou kunnen zien wanneer geïmplementeerd als een tagbody :

(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))

Hoewel tagbody en go niet vaak worden gebruikt, misschien vanwege "GOTO als schadelijk beschouwd", maar kan nuttig zijn bij het implementeren van complexe besturingsstructuren zoals staatsmachines. Veel iteratieconstructies breiden zich ook uit tot een impliciet tagbody . De body van een punttijd wordt bijvoorbeeld gespecificeerd als een reeks tags en formulieren.

Welke vorm te gebruiken?

Bij het schrijven van macro's die zich uitbreiden naar formulieren die eventueel kunnen worden gegroepeerd, is het de moeite waard om wat tijd door te brengen met het overwegen van welke groepconstructie moet worden uitgebreid.

Voor stijldefinitie vormen, bijvoorbeeld een vast-widget macro die meestal zal verschijnen als een top-level vorm, en dat een aantal defun s, defstruct s, enz., Maakt het meestal zinvol om een progn gebruiken, dus dat kind vormen verwerkt als formulieren op het hoogste niveau. Voor iteratievormen komt een impliciet tagbody vaker voor.

Bijvoorbeeld, het lichaam van dotimes , dolist , en breiden zich elk uit tot een impliciet tagbody .

Voor formulieren die een benoemde "stuk" code definiëren, is een impliciet blok vaak handig. Terwijl het lichaam van een defun zich bijvoorbeeld in een impliciete prognose bevindt , bevindt die impliciete prognose zich in een blok dat de naam van de functie deelt. Dat betekent dat return-from kan worden gebruikt om de functie te verlaten. Zo'n comp



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow