Search code examples
functional-programmingpattern-matchingcommon-lispclisp

Common Lisp - Pattern Matching


I'm trying to write a function which compare two lists in Common Lisp.

The sign ’&’ indicates any sequence of elements and the sign ’$’ indicates any single element.

(defun match (filter data)
     (cond
        ((atom filter) (eq filter data))
        ((atom data) NIL)
        ((eq (car filter) '|$|)
           (match (cdr filter) (cdr data)))
        ((eq (car filter) '|&|)
           (cond
             ((match (cdr filter) data))
             (data (match filter (cdr data)))))
        ((match (car filter) (car data))
            (match (cdr filter) (cdr data)))))

which works fine :

(match '(a b c) '(a b c)) ; => T
(match '(a $ c) '(a b c)) ; => T
(match '(a ff c) '(a b c)) ; => NIL
(match '(a & c) '(a z b c)) ; => T

I would like to add a new filter (with the% symbol) which allows the choice between different possible elements, for example

(match '(a %(b 1) c) '(a b c)) ; => T
(match '(a %(b 1) c) '(a 1 c)) ; => T
(match '(a %(b 1) c) '(a z c)) ; => NIL

Solution

  • You have to try all the elements in the sublist after % to see if any of them, combined with the rest of the filter, matches the data:

    (defun match (filter data)
         (cond
            ((atom filter) (eq filter data))
            ((atom data) NIL)
            ((eq (car filter) '|$|)
               (match (cdr filter) (cdr data)))
            ((eq (car filter) '|&|)
               (cond
                 ((match (cdr filter) data))
                 (data (match filter (cdr data)))))
            ((eq (car filter) '|%|)
              (some #'(lambda (f) (match (cons f (cddr filter)) data)) (cadr filter)) )
            ((match (car filter) (car data))
                (match (cdr filter) (cdr data)))))
    

    You should also add some checking steps to ensure that the filter is correctly formed, i.e. that the element after % is a list, etc...