Sök…


När behövs gruppering?

På vissa platser i Common Lisp utvärderas en serie former i ordning. Till exempel i kroppen av en defun eller lambda , eller kroppen av en dotimes . I dessa fall fungerar skrivning av flera formulär i ordning som förväntat. På några ställen, emellertid, såsom de och annars delar av en i förekommande uttryck, är endast en enda blankett tillåtna. Naturligtvis kan man faktiskt utvärdera flera uttryck på dessa platser. För dessa situationer behövs någon form av implicit av tydlig gruppform.

Progn

Det övergripande syftet speciell operatör progn används för att utvärdera noll eller flera former. Värdet på det sista formuläret returneras. I det följande utvärderas till exempel (skriv ut "hej) (och dess resultat ignoreras) och sedan utvärderas 42 och dess resultat ( 42 ) returneras:

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

Om det inte finns några former inom prognosen returneras noll :

(progn)
;=> NIL

Förutom att gruppera en serie formulär har progn också den viktiga egenskapen att om prognosformen är en toppnivåformulär, behandlas alla formulär i den som toppnivåformer. Detta kan vara viktigt när man skriver makron som expanderar till flera former som alla bör behandlas som toppnivåformulär.

Progn är också värdefullt genom att det returnerar alla värden för den sista formen. Till exempel,

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

Däremot returnerar vissa grupputtryck endast det primära värdet för den resultatproducerande formen.

Implicit Progns

Vissa former använder implicit progns för att beskriva deras beteende. Till exempel beskriver när och om inte makron, som i huvudsak är ensidiga om former, beskriver deras beteende i termer av en implicit progn . Detta betyder att en form som

(when (foo-p foo)
  form1
  form2)

utvärderas och tillståndet (foo-p foo) är sant, sedan grupperas formen 1 och formen 2 som om de fanns i en prognos . Utvidgningen av när makro är i huvudsak:

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

Prog1 och Prog2

Ofta är det bra att utvärdera flera uttryck och returnera resultatet från den första eller andra formen snarare än den sista. Detta är lätt att åstadkomma med låt och till exempel:

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

Eftersom detta formulär är vanligt i vissa applikationer, inkluderar Common Lisp prog1 och prog2 som är som prognos , men returnerar resultatet av den första respektive den andra formen. Till exempel:

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

En viktig skillnad mellan prog1 / prog2 och progn är emellertid att progn returnerar alla värden för den sista formen, medan prog1 och prog2 endast returnerar det primära värdet för den första och andra formen. Till exempel:

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

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

För flera värden med utvärdering av prog1- stil, använd istället multipel-värde-prog1 . Det finns ingen liknande multipel-värde-prog2 , men det är inte svårt att implementera om du behöver det.

Blockera

Den speciella operatörsblocket tillåter gruppering av flera Lisp former (som en implicit progn ) och det tar också ett namn att nämna blocket. När formulärerna inom blocket utvärderas, kan den speciella operatörens återvändande användas för att lämna blocket. Till exempel:

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

retur-från kan också förses med ett returvärde:

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

Namngivna block är användbara när en bit av kod har ett meningsfullt namn, eller när block är kapslade. I vissa sammanhang är det bara förmågan att återvända från ett block tidigt. I så fall kan du använda noll som blocknamn och gå tillbaka . Retur är precis som retur från , förutom att blocknamnet alltid är noll .

Obs: bifogade formulär är inte formulär på toppnivå. Det skiljer sig från progn , där de bifogade formerna av en toppnivå- progn fortfarande betraktas som toppnivåformer .

Tagbody

För massor av kontroll i gruppformer kan tagbody specialoperatör vara till stor hjälp. Formerna i en tagbody- form är antingen go-taggar (som bara är symboler eller heltal) eller former för att köra. Inom en tagbody används go specialoperatören för att överföra exekvering till en ny plats. Denna typ av programmering kan betraktas som ganska låg nivå, eftersom den tillåter godtyckliga körningsvägar. Följande är ett ordentligt exempel på hur en for-loop kan se ut när den implementeras som en 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))

Även om tagbody and go inte vanligtvis används, kanske på grund av "GOTO anses skadligt", men kan vara till hjälp när man implementerar komplexa kontrollstrukturer som statliga maskiner. Många iterationskonstruktioner expanderar också till en implicit tagbody . Till exempel specificeras kroppen av en dotimes som en serie taggar och formulär.

Vilken form ska jag använda?

När man skriver makron som expanderar till former som kan innebära gruppering är det värt att spendera lite tid på att tänka på vilken gruppkonstruktion man ska utvidga till.

För definitionsstilsformer, till exempel ett definiera widget- makro som vanligtvis kommer att visas som en toppnivåform, och som flera defun s, defstruera s, etc., är det vanligtvis vettigt att använda en prognos , så att barnformer är behandlas som toppnivåformer. För iterationsformer är en implicit tagbody vanligare.

Till exempel, kroppen av dotimes , dolist , och göra varje expandera till en implicit tagbody.

För formulär som definierar en benämnd "bit" av kod, är ett implicit block ofta användbart. Till exempel, medan kroppen av en defun finns i en implicit progn , är den implicita prognosen inom ett block som delar funktionens namn. Det betyder att retur från kan användas för att lämna funktionen. En sådan komp



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow