User Tools


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

cl:macros:case [2019/07/14 17:00]
cl:macros:case [2019/07/18 14:00] (current)
Line 1: Line 1:
 +====== Macro CASE, CCASE, ECASE ======
 +
 +====Syntax====
 +  * case //keyform {normal-clause}''​*''​ [otherwise-clause]//​ → //​result''​*''//​
 +  * ccase //keyplace {normal-clause}''​*''//​ → //​result''​*''//​
 +  * ecase //keyform {normal-clause}''​*''//​ → //​result''​*''//​
 +  ​
 +<​blockquote>​
 +//​normal-clause//​ ::= //(keys form''​*''​)//​
 +//​otherwise-clause//​ ::= //​({otherwise | t} form''​*''​)//​
 +//clause// ::= //​normal-clause | otherwise-clause//​
 +</​blockquote>​
 +
 +====Arguments and Values====
 +  * //keyform// - a //​[[CL:​Glossary:​form]]//;​ evaluated to produce a //​test-key//​.
 +  * //​keyplace//​ - a //​[[CL:​Glossary:​form]]//;​ evaluated initially to produce a //​test-key//​. Possibly also used later as a //​[[CL:​Glossary:​place]]//​ if no //keys// match.
 +  * //​test-key//​ - an object produced by evaluating //keyform// or //​keyplace//​.
 +  * //keys// - a //​[[CL:​Glossary:​designator]]//​ for a //​[[CL:​Glossary:​list]]//​ of //​[[CL:​Glossary:​object|objects]]//​. In the case of **case**, the //​[[CL:​Glossary:​symbol|symbols]]//​ ''​[[CL:​Constant Variables: t]]''​ and ''​otherwise''​ may not be used as the //keys// //​[[CL:​Glossary:​designator]]//​. To refer to these //​[[CL:​Glossary:​symbol|symbols]]//​ by themselves as //keys//, the designators ''​([[CL:​Constant Variables: t]])''​ and ''​(otherwise)'',​ respectively,​ must be used instead.
 +  * //forms// - an //​[[CL:​Glossary:​implicit progn]]//.
 +  * //results// - the //​[[CL:​Glossary:​value|values]]//​ returned by the //forms// in the matching //clause//.
 +
 +====Description====
 +These //​[[CL:​Glossary:​macro|macros]]//​ allow the conditional execution of a body of //forms// in a //clause// that is selected by matching the //​test-key//​ on the basis of its identity.
 +
 +The //keyform// or //​keyplace//​ is //​[[CL:​Glossary:​evaluate|evaluated]]//​ to produce the //​test-key//​.
 +
 +Each of the //​normal-clauses//​ is then considered in turn. If the //​test-key//​ is the //​[[CL:​Glossary:​same]]//​ as any //​[[CL:​Glossary:​key]]//​ for that //clause//, the //forms// in that //clause// are //​evaluated//​ as an //​[[CL:​Glossary:​implicit progn]]//, and the //​[[CL:​Glossary:​value|values]]//​ it returns are returned as the value of the **case**, **ccase**, or **ecase** //​[[CL:​Glossary:​form]]//​.
 +
 +These //​[[CL:​Glossary:​macro|macros]]//​ differ only in their //​[[CL:​Glossary:​behavior]]//​ when no //​normal-clause//​ matches; specifically:​
 +
 +===case===
 +
 +If no //​normal-clause//​ matches, and there is an //​otherwise-clause//,​ then that //​otherwise-clause//​ automatically matches; the //forms// in that //clause// are //​evaluated//​ as an //​[[CL:​Glossary:​implicit progn]]//, and the //​[[CL:​Glossary:​value|values]]//​ it returns are returned as the value of the **case**.
 +
 +If there is no //​otherwise-clause//,​ **case** returns **[[CL:​Constant Variables:​nil]]**.
 +
 +===ccase===
 +
 +If no //​normal-clause//​ matches, a //​[[CL:​Glossary:​correctable]]//​ //​[[CL:​Glossary:​error]]//​ of type **[[CL:​Types:​type-error]]** is signaled. The offending datum is the //​test-key//​ and the expected type is //​[[CL:​Glossary:​type equivalent]]//​ to ''​(member //key1// //key2// ...)''​. The //​[[CL:​Glossary:​restart]]//​ **[[CL:​Restarts:​store-value]]** can be used to correct the error.
 +
 +If the //​[[CL:​Glossary:​restart]]//​ **[[CL:​Restarts:​store-value]]** is invoked, its //​[[CL:​Glossary:​argument]]//​ becomes the new //​test-key//,​ and is stored in //​keyplace//​ as if by ''​(setf //​keyplace//​ //​test-key//​)''​. Then **ccase** starts over, considering each //clause// anew.
 +
 +The subforms of //​keyplace//​ might be evaluated again if none of the cases holds.
 +
 +===ecase===
 +
 +If no //​normal-clause//​ matches, a //​[[CL:​Glossary:​non-correctable]]//​ //​[[CL:​Glossary:​error]]//​ of type **[[CL:​Types:​type-error]]** is signaled. The offending datum is the //​test-key//​ and the expected type is //​[[CL:​Glossary:​type equivalent]]//​ to ''​([[CL:​Types:​member]] //key1// //key2// ...)''​.
 +
 +Note that in contrast with **ccase**, the caller of **ecase** may rely on the fact that **ecase** does not return if a //​normal-clause//​ does not match.
 +
 +====Examples====
 +<​blockquote>​
 +([[CL:​Macros:​dolist]] (k '(1 2 3 :four #\v () [[CL:​Constant Variables:​t]] '​other))
 +  ([[CL:​Functions:​format]] [[CL:​Constant Variables:​t]] "~S " ​
 +    (case k 
 +      ((1 2) '​clause1) ​
 +      (3 '​clause2) ​
 +      ([[CL:​Constant Variables:​nil]] '​no-keys-so-never-seen) ​
 +      (([[CL:​Constant Variables:​nil]]) '​nilslot) ​
 +      ((:four #\v) '​clause4) ​
 +      (([[CL:​Constant Variables:​t]]) '​tslot) ​
 +      (otherwise '​others))))
 +<​o>​CLAUSE1 CLAUSE1 CLAUSE2 CLAUSE4 CLAUSE4 NILSLOT TSLOT OTHERS </o>
 +<​r>​[[CL:​Constant Variables:​NIL]] </r>
 +([[CL:​Macros:​defun]] add-em (x) 
 +  ([[CL:​Functions:​apply]] #'​[[CL:​Functions:​math-add|+]] ([[CL:​Functions:​mapcar]] #'​decode x))) <​r>​ADD-EM </r>
 +([[CL:​Macros:​defun]] decode (x) 
 +  (ccase x 
 +    ((i uno) 1) 
 +    ((ii dos) 2) 
 +    ((iii tres) 3) 
 +    ((iv cuatro) 4))) <​r>​DECODE </r>
 +(add-em '(uno iii)) <r>4 </r>
 +(add-em '(uno iiii))
 +<​e>​Error:​ The value of X, IIII, is not I, UNO, II, DOS, III,
 +TRES, IV, or CUATRO.
 +1: Supply a value to use instead.
 +2: Return to Lisp Toplevel.
 +Debug> :CONTINUE 1
 +Value to evaluate and use for X: '​IV</​e>​
 +<​r>​5</​r>​
 +</​blockquote>​
 +
 +====Side Effects====
 +The debugger might be entered. If the //​[[CL:​Glossary:​restart]]//​ **[[CL:​Restarts:​store-value]]** is invoked, the //​[[CL:​Glossary:​value]]//​ of //​keyplace//​ might be changed.
 +
 +====Affected By====
 +**ccase** and **ecase**, since they might signal an error, are potentially affected by existing //​handlers//​ and **[[CL:​Variables:​*debug-io*]]**.
 +
 +====Exceptional Situations====
 +**ccase** and **ecase** signal an error of type **[[CL:​Types:​type-error]]** if no //​normal-clause//​ matches.
 +
 +====See Also====
 +  * **[[CL:​Macros:​cond|Macro COND]]**
 +  * **[[CL:​Macros:​typecase|Macro TYPECASE]]**
 +  * **[[CL:​Macros:​setf|Macro SETF]]**
 +  * {\secref\GeneralizedReference}
 +
 +====Notes====
 +<​blockquote>​
 +(case //​test-key//​ {((//​key''​*''//​) //​form''​*''//​)}''​*''​) ​
 +  ≡ ([[CL:​Special Operators:​let]] ((#​1=#:​g0001 //​test-key//​)) ([[CL:​Macros:​cond]] {(([[CL:​Functions:​member]] #1# '​(//​key''​*''//​)) //​form''​*''//​)}''​*''​))
 +</​blockquote>​
 +
 +The specific error message used by **ecase** and **ccase** can vary between implementations. In situations where control of the specific wording of the error message is important, it is better to use **case** with an //​otherwise-clause//​ that explicitly signals an error with an appropriate message.
 +