我不介意承认这是一项令我难过的家庭作业。任何向正确方向的推动都会很有用。
我需要编写一个返回两个给定列表的并集的函数。我相信我的逻辑是合理的,但Lisp语法正在推动我的发展。
到目前为止我的解决方案就是这个。
(defun inList (e L)
(cond
((null L)
nil)
((equal (first L) e)
T)
(T
(inList e (rest L)))))
(defun union2 (L1 L2)
(cond
((null L2)
L1)
((not (inList (first L2) L1))
(append (union2 L1 (rest L2)) (first L2)))
(T
(union2 L1 (rest L2)))))
当我使用空列表作为第二个参数测试函数,或者第二个参数是一个列表,其中每个项目都是第一个列表的成员时,它可以正常工作。
然而,在测试时
(union2 '(1 2 3) '(4 5 6))
我得到6 is not of type list
。
我非常确定我的错误在:(append (union2 L1 (rest L2)) (first L2)
就在那时,(first L2)
显然不是一个列表。但是,将其写为((first L2))
会给我Badly formed lambda
。
正如我所说,任何提示或指示都会受到赞赏。
答案 0 :(得分:5)
您确实需要缩进代码。大多数Lisp用户都理解以下内容。它是自动且正确缩进的,并且没有自己的括号。
(defun union2 (L1 L2)
(cond
((null L2) L1)
((not (inList (first L2) L1))
(append (union2 L1 (rest L2))
(first L2)))
(T (union2 L1 (rest L2)))))
所以你认为,(append ... (first L2))
是个问题?
append
期望列表作为参数。 l2
的第一个元素可能不是列表。
如何制作清单?
(append l1 l2 ...)
返回附加列表的列表(cons item l1)
会返回一个列表,其中item
已添加到l1
的前面。(list e1 ...)
返回包含项e1
...作为元素的列表。它需要零个或多个参数。上述其中一项应该可以帮助您从项目中创建新列表。
另请注意,附加到列表末尾不是Lisp中的有效列表操作。首先将元素添加到列表的前面。
答案 1 :(得分:2)
Rainer Joswig's answer是正确的,因为这是家庭作业,所以它可能是最合适的,因为您可能不被允许使用某些功能。否则你可以使用Common Lisp的union
函数:
(union '(1 2 3) '(4 5 6))
;=> (3 2 1 4 5 6)
使用它,你可以编写如下的定义(虽然你可能不会得到它的信任,除非有人说,“最后,一个学生撇去标准库!”):
(defun easy-union (list1 list2)
(union list1 list2))
但是,一般情况下,如果使用标准函数adjoin
,它可能会更容易一些,该函数接受一个元素并将其添加到列表中,除非该元素已经在列表中。使用它,您不需要编写inList
函数(您可能已使用member
):
(defun recursive-union (list1 list2)
(if (endp list1)
list2
(recursive-union (rest list1)
(adjoin (first list1)
list2))))
(recursive-union '(1 2 3) '(4 5 6))
;=> (3 2 1 4 5 6)
(recursive-union '(1 2 3) '(2 3 4))
;=> (1 2 3 4)
这是尾递归,你也可以迭代地做同样的事情(如果你的特定实现没有执行尾调用优化,这可能是一个好主意):
(defun iterative-union (list1 list2)
(do ((list2 list2 (adjoin (first list1) list2))
(list1 list1 (rest list1)))
((endp list1) list2)))
(iterative-union '(1 2 3) '(4 5 6))
;=> (3 2 1 4 5 6)
(iterative-union '(1 2 3) '(2 3 4))
;=> (1 2 3 4)
还有一些其他有用的库函数可以帮助你,例如set-difference
。例如,基于身份A∪B=(A / B)∪B,您可以写:
(defun another-union (list1 list2)
(nconc (set-difference list1 list2)
list2))
您可能会被禁止使用set-difference
,当然,remove-if
和member
有助于模拟它:
(defun yet-another-union (list1 list2)
(nconc (remove-if #'(lambda (e)
(member e list2))
list1)
list2))
答案 2 :(得分:0)
FWIW,这个迭代版本是我大部分时间都在使用的版本。它与Emacs cl-union
中的union
(以前称为cl-seq.el
)基本相同,但没有关键字处理。
(defun icicle-set-union (list1 list2)
"Combine LIST1 and LIST2 using a set-union operation.
The result list contains all items that appear in either LIST1 or
LIST2. This is a non-destructive function; it copies the data if
necessary."
(cond ((null list1) list2)
((null list2) list1)
((equal list1 list2) list1)
(t
(unless (>= (length list1) (length list2))
(setq list1 (prog1 list2 (setq list2 list1)))) ; Swap them.
(while list2
(unless (member (car list2) list1) (setq list1 (cons (car list2) list1)))
(setq list2 (cdr list2)))
list1)))
答案 3 :(得分:0)
(defun my-union (x y)
(cond ((null x) y)
((member (car x) y) (my-union (cdr x) y))
(t (cons (car x) (my-union (cdr x) y)))))
CL-USER> (my-union '(1 3 5 7) '(2 4 6 8))
0: (MY-UNION (1 3 5 7) (2 4 6 8))
1: (MY-UNION (3 5 7) (2 4 6 8))
2: (MY-UNION (5 7) (2 4 6 8))
3: (MY-UNION (7) (2 4 6 8))
4: (MY-UNION NIL (2 4 6 8))
4: MY-UNION returned (2 4 6 8)
3: MY-UNION returned (7 2 4 6 8)
2: MY-UNION returned (5 7 2 4 6 8)
1: MY-UNION returned (3 5 7 2 4 6 8)
0: MY-UNION returned (1 3 5 7 2 4 6 8)
(1 3 5 7 2 4 6 8)
这是我的递归联合代码。
希望它可以帮助你或任何人