需要在lisp中编写一个union函数,它将两个列表作为参数,并返回一个列表,该列表是两者的并集,没有重复。订单应与输入列表的顺序一致
例如:如果输入是'(a b c)和'(e c d),结果应为'(a b c e d)
这是我到目前为止所拥有的
(defun stable-union (x y)
(cond
((null x) y)
((null y) x))
(do ((i y (cdr i))
(lst3 x (append lst3
(cond
((listp i)
((null (member (car i) lst3)) (cons (car i) nil) nil))
(t (null (member i lst3)) (cons i nil) nil)))))
((null (cdr i)) lst3)))
我的错误是有一个"非法的功能对象"与段(null(member(car i)lst3))
么?
答案 0 :(得分:1)
错误是因为您正在尝试执行评估(null (member (car i) lst3))
的结果。在 cond 表达式中,如果 i 是一个列表,那么它会尝试评估表达式
((null (member (car i) lst3)) (cons (car i) nil) nil))
并返回结果。表达式中的第一个元素应该是一个函数,但是
(null (member (car i) lst3))
将返回一个布尔值。因此失败了。您的代码结构需要注意。你错过的是你需要一个内在的 cond 。
顺便说一句,如果你以递归的方式做这件事,这将是一个更清晰的功能。
我是Schemer而不是Lisper,但我有点想一想。这是递归实现的框架:
(defun stable-union (x y)
(cond
((null x) y)
((null y) x)
((listp y)
(cond
((member (car y) x) (stable-union ??? (???)))
(t (stable-union (append x (??? (???))) (cdr y)))))
((not (member y x)) (append x (list y)))
(t x)))
(编辑在倒数第二行纠正简单的错位,感谢Will Ness发现它)
答案 1 :(得分:1)
你的私奔都混乱了:
(defun stable-union (x y)
(cond
((null x) y)
((null y) x) ) END OF COND form - has no effect
(do ((i y (cdr i))
^^
(lst3 x (append lst3
(cond
((listp i)
( (null (member (car i) lst3))
^^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ called as a function
(cons (car i) nil) with two arguments
nil ) )
^^
(t NEXT 3 forms have no effect
(null (member i lst3))
(cons i nil)
nil )))) )
^^
((null (cdr i)) lst3)))
这是您可能想要的代码,更正了括号,并在需要时添加了一些if
:
(defun stable-union (x y)
(cond
((null x) y)
((null y) x)
(t
(do ((i y (cdr i))
(lst3 x (append lst3
(cond
((listp i)
(if (null (member (car i) lst3))
(cons (car i) nil)
nil))
(t
(if (null (member i lst3))
(cons i nil)
nil))))))
((null (cdr i)) lst3)))))
此代码仍存在问题。您的do
逻辑错误,如果它只包含一个元素,它会跳过y
中的第一个元素。无论是否需要,您都会一直致电append
。请注意,调用(append lst3 nil)
会在lst3
中复制顶级缺点单元格,完全是多余的。
你所拥有的这么长的陈述通常放在do
正文中,而不是放在do
的局部变量的更新表格内。
但您可以在适当的情况下使用更专业的do
形式。在这里使用dolist
是很自然的。在"wvxvw"'s lead on using hash-tables for membership testing之后,我们写道:
(defun stable-union (a b &aux (z (list nil)))
(let ((h (make-hash-table))
(p z))
(dolist (i a)
(unless (gethash i h)
(setf (cdr p) (list i) p (cdr p))
(setf (gethash i h) t)))
(dolist (i b (cdr z))
(unless (gethash i h)
(setf (cdr p) (list i) p (cdr p))
(setf (gethash i h) t)))))
使用我称之为“head-sentinel”的技术(z
变量预先初始化为单个列表)允许自上而下的列表构建的代码的大大简化,代价是分配一个额外的cons
单元格。
答案 2 :(得分:0)
(remove-duplicates (append '(a b c) '(e c d)) :from-end t)
答案 3 :(得分:0)
因为你从do
开始,并且因为递归解决方案会更糟,所以你可以做到这一点:
(defun union-stable (list-a list-b)
(do ((i list-b (cdr i))
filtered back-ref)
((null i) (append list-a back-ref))
(unless (member (car i) list-a)
(if back-ref
(setf (cdr filtered) (list (car i))
filtered (cdr filtered))
(setf back-ref (list (car i))
filtered back-ref)))))
这仍然是二次时间,并且行为是这样的:如果第一个列表有重复项,或者第二个列表有重复项(不在第一个列表中),它们将保留。我不确定将此函数称为“联合”是多么公平,但如果它们在尝试统一它们之前有重复,则必须定义如何处理列表。
如果您对结果感兴趣,而不仅仅是锻炼,那么这就是您可能做过的事情。请注意,即使元素在输入列表中重复,它也将确保元素是唯一的。
(defun union-stable-hash (list-a list-b)
(loop for c = (car (if list-a list-a list-b))
with back-ref
with hash = (make-hash-table)
for key = (gethash c hash)
with result
do (unless key
(if back-ref
(setf (cdr result) (list c)
result (cdr result))
(when (or list-a list-b)
(setf back-ref (list c)
result back-ref)))
(setf (gethash c hash) t))
do (if list-a (setf list-a (cdr list-a))
(setf list-b (cdr list-b)))
do (unless (or list-a list-b)
(return back-ref))))