我正在尝试编写一个比较两个家庭作业列表的功能。当函数运行时,它应该是这样的;(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
答案 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))))
的非常类似的方法
(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
也不会出现在结果中。