Case-match Documentation
case-match
Pattern matching is a mechanism for checking a variable symbol against a pattern. It can also deconstruct the variable into its constituent parts. We can think it as a more powerful version of cond in acl2.
General Form
(case-match x
(pat1 body1)
(pat2 body2)
...
(patk bodyk))
Where x is the variable symbol that we are checking against. The pati are structural patterns, and the bodyi are terms. case-match returns the value of the bodyi that corresponds to the first pati matching of x, or nil if none of the pati matches x. However, it is in general a good practice to have a full coverage of cases. case-match is similar to cond except the conditions are replaced by structural patterns.
Pattern Language:
- With a few special exceptions described in the Exceptions section below, matching requires that the
consstructure of x be isomorphic to that of the pattern (for example, the structure of(cons 2 nil)is isomorphic to the pattern(cons a b)), down to the atoms in the pattern. - Non-symbol atoms in the pattern match only themselves (e.g.
(cons ‘a nil)does not match the pattern(cons B nil). Because ’a does not match B, it only matches A). - Symbols in the pattern denote variables which match anything and which are bounded by a successful match to the corresponding substructure of x.
- Variables that occur more than once must match the same (
equal) structure in every occurrence (e.g.(A 2 2)matches the pattern(y z z), but(A 2 3)does not).
Exceptions
& Matches anything (like a wild card). & is not bounded,
and repeated occurrences of & in a pattern may match
different structures.
nil, t These symbols cannot be bound and match only their
global values.
!sym where sym is a symbol that is already bound in the
context of the case-match, matches only the
current binding of sym.
'obj Matches only itself. This is the same as (QUOTE obj).
Sample Patterns and Matches
Below are some sample patterns and examples of their matches and non-matches.
pattern matches non-matches
(y z z) (ABC 3 3) (ABC 3 4) ; 3 is not 4
(a . rst) (A . B) nil ; NIL is not in the form (a . rst)
(‘a (G x) 3) (A (H 4) 3) (B (G X) 3) ; ‘a matches only itself.
(& t & !x) ((A) T (B) (C)) ; provided x is '(C)
(a b c) '(1 2 1) '(1 2) ; '(1 2) is not isomorphic to (a b c)
case-match example
Consider a math comparison expressions (compexpr) that can take the form of a rational number or a list with two rational numbers and a comparison operator (one of ‘>, ‘<, or ‘=). To put this in acl2 language, we have
(defdata compop (enum '(> < =)))
(defdata compexpr (oneof rational
(list rational rational compop)))
Suppose we want to recognize a math comparison expression x and evaluate it. The following function with case-match would do:
(definec evalcomp (x :compexpr) :bool
(case-match x
((a b '<) (< a b))
((a b '=) (== a b))
((a b '>) (> a b))
(& nil))) ;; this covers the "not a list" case of the data definition.
;; If we needed to distinguish between different kinds of
;; non-list values, we could use a cond here
Note: If you are not going to use a variable in your body term, do not give it a name. Otherwise ACL2S will complain about it. For example, if I only care about the first in the pattern (cons first rest), I can structure my pattern as (cons first &).
Bad example:
(case-match tl
((first . rest) first) ;; ACL2S will complain because rest is not used in the body term
(& nil))
Another bad example:
(case-match tl
((a b c d) (+ a b)) ;; ACL2S will complain because c and d are not used in the body term
(& 0))
Use & to stop ACL2S from complaining about unused variables:
(case-match tl
((a b & &) (+ a b)) ;; The pattern still matches list with 4 elements, but only the first 2 elements are named and used
(& 0))