使用通配符比较列表

时间:2012-11-16 21:14:23

标签: list recursion lisp

我正在尝试编写一个比较两个家庭作业列表的功能。当函数运行时,它应该是这样的;(cmp'(cat?x mat?x)'(cat bat mat bat))=> t;(cmp'(cat?x mat?x)'(cat bat mat sat))=>零。意思是在第一个列表中等于?x而第二个?x如果两者都指向相同的值则返回true。 当我运行程序现在给我“错误,同时解析特殊形式的参数如果:元素数量无效”如果你能给我一些反馈,这是我的代码。感谢。

;cmp algorithm
;1 if the both lists are empty return true
;2 if only one of the lists is empty return fasle
;3 compare first of the list1 and the first of list2
;if equal go on to the rest of the list with recursive call else return false   

(defun cmp (list1 list2)
(setq y '())
(setq z '())
(defparameter *counter* 0)
  (cond 
   ((and (null list1) (null list2))
    t 
    )
   ((or (null list1) (null list2))
    nil
    )
    ((or (eq (first list1) (first list2)) 
         (eq (first list1) '?x)  )
     (cmp (rest list1) (rest list2) )

        ;if (first list is equal to '?x)
        ;set the counter to 1
        ;give the value of (first(rest list2)) to y 
        ;if (first list is equal to '?x) again
        ;set the counter to 2
        ;give the value of (first (rest list2)) to z
        ;i need to compare y and z if eq return true

     (if (eq (first list1) '?x) 
        (princ (first list1 ))
        (princ (first(rest list2)))

        (1+ *counter*)
        (set y (first(rest list2)))

        (if (= *counter* 2)
        (set z (first (rest list2)))    
            )       
        )

        (if (= y z) t)      
     )
   (t
    nil)
   )
  )





;(cmp ‘(cat ?x mat ?x) ‘(cat bat mat bat))  =>  t 
  ;(cmp ‘(cat ?x mat ?x) ‘(cat bat mat sat))  =>  nil

2 个答案:

答案 0 :(得分:1)

虽然阅读本书或文档肯定会有所帮助,但有时会查看示例代码,特别是在您已经了解问题后也可以提供帮助。所以这是一个朴实无华的直接解决方案:

(defun compare-using-wildcards (pattern matched)
  (loop for p in pattern
     for m in matched
     with keys = (make-hash-table)
     do (unless (eql p m)               ; Doesn't matter
                                        ; if it starts with ?
                                        ; we still don't consider
                                        ; it a wildcart symbol, if
                                        ; it matches the symbol in
                                        ; the other list.
          (if (and (symbolp p)
                   (char= (aref (symbol-name p) 0) #\?))
              (multiple-value-bind (registered exists)
                  (gethash p keys)
                (if exists
                    (unless (eql registered m)
                      (return))
                    (setf (gethash p keys) m)))
              (return)))
     finally (return t)))

(compare-using-wildcards '(cat ?x mat ?x) '(cat bat mat bat)) ; T
(compare-using-wildcards '(cat ?x mat ?x) '(cat bat mat sat)) ; NIL
(compare-using-wildcards '(cat ?x mat ?y) '(cat bat mat sat)) ; T
(compare-using-wildcards '(cat ?x max ?y) '(cat bat mat sat)) ; NIL

但是有很多方法可以做到这一点!例如,如果已知列表很短,则可以通过destructuring-bind执行此操作。或者,您可以编写一个“zip”函数(一个更高阶的函数,将多个列表中的单元格提供给其他函数,直到它返回非零结果),依此类推。


这是一个有点人为的例子。好吧,它看起来应该可行,除非我错过了一些角落案例。它会使用通配符将多个列表与列表进行比较:

(every (let ((keys (make-hash-table)))
       #'(lambda (&rest elements)
           (let ((wildcard (car elements)))
             (if (and (symbolp wildcard)
                      (char= (aref (symbol-name wildcard) 0) #\?))
                 (let ((replacement (gethash wildcard keys))
                       (i -1))
                   (if replacement
                       (every #'(lambda (x)
                                  (eql x (aref replacement (incf i))))
                              (cdr elements))
                       (setf (gethash wildcard keys)
                             (coerce (cdr elements) 'vector))))
                 (every #'(lambda (x) (eql x wildcard)) elements)))))
     '(cat ?x mat ?x)
     '(cat bat mat bat)
     '(cat bar mat bar)
     '(cat bank mat bank)
     '(cat bass mat boss))

答案 1 :(得分:1)

你快到了。您遗漏了如何在第一个字符为?的任何符号上以及如何将匹配传递给递归调用的一般匹配。

您需要在两次通话之间保存匹配。一种可能的方法是将它们传递给可选的association list匹配项:

(defun cmp (list1 list2 &optional matches)
  (cond ((and (null list1) (null list2))
         t)
        ((or (null list1) (null list2))
         nil)
        ((and (symbolp (first list1))
              (plusp (length (symbol-name (first list1))))
              (eql (char (symbol-name (first list1)) 0) #\?))
         (let ((assoc (assoc (first list1) matches)))
           (cond ((null assoc)
                  (cmp (rest list1) (rest list2)
                       (list* (cons (first list1) (first list2))
                              matches)))
                 ((eql (cdr assoc) (first list2))
                  (cmp (rest list1) (rest list2) matches)))))
        ((eql (first list1) (first list2))
         (cmp (rest list1) (rest list2) matches))))

使用dynamic variable

的非常类似的方法
(defvar *matches* '())

(defun cmp (list1 list2)
  (cond ((and (null list1) (null list2))
         t)
        ((or (null list1) (null list2))
         nil)
        ((and (symbolp (first list1))
              (plusp (length (symbol-name (first list1))))
              (eql (char (symbol-name (first list1)) 0) #\?))
         (let ((assoc (assoc (first list1) matches)))
           (cond ((null assoc)
                  (let ((*matches* (list* (cons (first list1) (first list2))
                                          *matches*)))
                    (cmp (rest list1) (rest list2))))
                 ((eql (cdr assoc) (first list2))
                  (cmp (rest list1) (rest list2))))))
        ((eql (first list1) (first list2))
         (cmp (rest list1) (rest list2)))))

两者都可以这样称呼:

> (cmp '(?x b ?x d ?y f ?y h)
       '(a  b c  d  e f g  h))
nil
> (cmp '(?x b ?x d ?y f ?y h)
       '(a  b a  d  e f e  h))
t

但是,如果您已经开始使用匹配关联列表,则会按如下方式调用第一个匹配列表:

> (cmp '(?x ?y)
       '(a  b)
       '((?x . a)))
t

虽然第二个是这样使用的:

> (let ((*matches* '((?x . a))))
    (cmp '(?x ?y)
         '(a  b)))
t

练习:让cmp始终与'?(名称仅为问号的符号)匹配。

如果你想要一个元素,但是你想忽略它,这可能很有用。


练习:使cmp更有用,并返回找到的关联列表,而不是t

> (cmp '(?x ?y)
       '(a  b))
((?x . a)
 (?y . b))

;;; Assuming option one
> (cmp '(?x ?y)
       '(a  b)
       '((?x . a)
         (?z . c)))
((?x . a)
 (?y . b))
> (cmp '(?x ?y)
       '(c  b)
       '((?x . a)
         (?z . c)))
nil

这个想法是只返回找到的关联,而不是未使用的关联。因此,即使第二个测试返回非nil?z也不会出现在结果中。