Arc Forumnew | comments | leaders | submitlogin
Pattern matching
2 points by taodach 5415 days ago | 6 comments
Hello

I wrote a matching pattern function and would like to share in order to improve it (remove the compiler warnings for example, see below).

I started with the destruc function found in onLisp book by Paul Graham.

I modified the destruc function in order to not use the nth function (I found it is not efficient); instead I bind partial results in generated symbols.

Also, unlike the dbind (see onLisp), the matching pattern function returns nil if the structure is not matched otherwise it runs the given parameter body; this allows us to check for alternative matches (used in compiler).

Any matching symbol x can be typed and must be of the form (:type x #'name) where name is a boolean function with one argument (eg. numberp, consp, symbolp, atom, ...)

Examples:

A pattern is a tree of symbols with possibly typed symbols

  ? (with-match-destruc (a (b (:type c #'symbolp))) (2 (3 4)) (list a b c))
  ;Compiler warnings :
  ;   In an anonymous lambda form: Unused lexical variable #:G2554
  ;   In an anonymous lambda form: Unused lexical variable #:G2555
  NIL
  ? (with-match-destruc (a (b (:type c #'symbolp))) (2 (3 x)) (list a b c))
  ;Compiler warnings :
  ;   In an anonymous lambda form: Unused lexical variable #:G2565
  ;   In an anonymous lambda form: Unused lexical variable #:G2566
  (2 3 X)
  ? (with-match-destruc (a (b c) (d (:type e #'symbolp)) f)
      (2 (3 4) (5 x) 6) (list a b c d e f))
  ;Compiler warnings :
  ;   In an anonymous lambda form: Unused lexical variable #:G2598
  ;   In an anonymous lambda form: Unused lexical variable #:G2601
  ;   In an anonymous lambda form: Unused lexical variable #:G2604
  (2 3 4 5 X 6)
  ?

  (defmacro with-match-destruc (pat seq &body body)
    (let ((x (gensym)))
      `(block nil 
        (let* ((,x ',seq) 
         ,@(match-destruc pat x))
    ,@body))))

  (defun match-destruc (pat seq)
    (cond
      ((null pat) nil)
      ((symbolp pat)
       `(,pat (if (symbolp ,seq) ,seq (return-from nil nil))))
      ((atom pat)
       (list `(,pat (if (atom ,seq) ,seq (return-from nil nil)))))
      ((eq :type (car pat))
       (list `(,(second pat)
               (if (and (atom ,seq) (funcall ,(third pat) ,seq))
                 ,seq
                 (return-from nil nil)))))
      (t
       (let ((r (let* ((p   (car pat))
                       (var (gensym))
                       (rec (if (null (cdr pat))
                              nil
                              (cons `(,var (if (consp ,seq)
                                             (cdr ,seq)
                                             (return-from nil nil)))
                                    (match-destruc (cdr pat) var)))))
                  (if (atom p)
                    (cons `(,p (if (consp ,seq)
                                 (car ,seq)
                                 (return-from nil nil)))
                          rec)
                    (if (eq (car p) :type)
                        (cons `(,(second p) 
                                 (if (and (consp ,seq)
                                          (funcall ,(third p) (car ,seq)))
                                   (car ,seq) 
                                   (return-from nil nil)))
                              rec)
                        (append (match-destruc p 
                                              `(if (consp ,seq)
                                                (car ,seq)
                                                (return-from nil nil)))
                                rec))))))
             (if (null (cdr pat))
               (cons `(,(gensym) ; dummy (should be declared to ignore)
                       (if (not (and (consp ,seq) (null (cdr ,seq))))
                         (return-from nil nil)))
                     r)
               r)))))

Kind regards Taoufik


1 point by taodach 5414 days ago | link

I liked the idea to use the form (:type var typename) like (:type x '(unsigned-byte 32)) and then replace funcall by typep (this was hinted to me by Greg Pfeil in openmcl-devel).

  (defun match-destruc (pat seq)
  (cond
    ((null pat) nil)
    ((symbolp pat) `(,pat (if (symbolp ,seq) ,seq (return-from nil nil))))
    ((atom pat)
     (list `(,pat
	     (if (atom ,seq) ,seq (return-from nil nil)))))
    ((eq :type (car pat))
     (list `(,(second pat)
	     (if (and (atom ,seq) (typep ,seq ,(third pat)))
		 ,seq (return-from nil nil)))))
    (t
     (let ((r 
	    (let* ((p (car pat))
		   (var (gensym))
		   (rec (if (null (cdr pat))
			    nil
			    (cons `(,var (if (consp ,seq) (cdr ,seq)
					     (return-from nil nil)))
				  (match-destruc (cdr pat) var)))))
	      (if (atom p)
		  (cons `(,p (if (consp ,seq) (car ,seq)
				 (return-from nil nil))) rec)
		  (if (eq (car p) :type)
		      (cons `(,(second p) 
			      (if (and (consp ,seq) 
				       (typep (car ,seq) ,(third p)))
				  (car ,seq) 
				  (return-from nil nil))) rec)
		      (append (match-destruc 
			       p 
			       `(if (consp ,seq)
				    (car ,seq)
				    (return-from nil nil)))
			      rec))))))
       (if (null (cdr pat))
	   (cons `(,(gensym) ; dummy (should be declared to ignore)
		   (if (not (and (consp ,seq) (null (cdr ,seq))))
		       (return-from nil nil)))
		 r)
	   r)))))
Usage: ? (with-match-destruc (a (b c) (d (:type e 'symbol)) f) (2 (3 4) (5 x) 6) (list a b c d e f))

Please note that the complexity of match-destruc is O(n) and the generated code is also O(n)

Taoufik

-----

2 points by rocketnia 5413 days ago | link

I disagree with that change. You've made this...

  (:type var '(unsigned-byte 32))
...do what this used to do...

  (:type var (lambda (x) (typep x '(unsigned-byte 32))))
...but I don't think there's a replacement for this:

  (:type var (lambda (x) (and (numberp x) (<= 10 x))))
Maybe you should have both a (:type var 'symbol) form and a (:test var #'symbolp) form. ^_^

-----

1 point by taodach 5415 days ago | link

Can someone write this in Arc?

-----

1 point by rocketnia 5414 days ago | link

I did, but then my computer ate it! ><;; Sorry, I don't think I have time to try again. Maybe someone else will?

Here's a bit of a Rosetta Stone to help you out, or at least to give you an idea of how it would look:

  ; Common Lisp
  (let* ((a #'list)
         (b 1)
         (c 2))
    (begin nil
      (cond
        (a (funcall a 3 4 5))
        (b (return-from nil nil))
        (t 6))))
  
  ; Arc
  (withs (a list
          b 1
          c 2)
    (point foo
      (if a (a 3 4 5)
          b (foo nil)
            6)))
The rest should be a straightforward process of finding Arc functions that do the same things as the Common Lisp functions do. Here are some useful links for that: http://files.arcfn.com/doc/ http://files.arcfn.com/doc/fnindex.html They're a bit out-of-date in a couple of places though. :/

-----

1 point by taodach 5415 days ago | link

How do I format correctly the message body?

-----

1 point by aw 5415 days ago | link

Indent code blocks with two spaces to have them formatted as code.

http://arclanguage.org/formatdoc

-----