我有一个lisp作业,我很难用它。
我必须编写一个执行联合操作的函数。该函数采用2个输入,以原子或列表的形式和每个元素的联合,保留顺序并剥离所有级别的括号。
该功能的输出:
(my-union 'a 'b) ;; (a b)
(my-union 'a '(b)) ;; (a b)
(my-union '(a b) '(b c)) ;; (a b c)
(my-union '(((a))) '(b(c((d e))a))) ;; (a b c d e)
我对lisp很新。 这是我到目前为止所写的内容,它只适用于第三个例子:
(defun new-union (a b)
(if (not b)
a
(if (member (car b) a)
(new-union a (cdr b))
(new-union (append a (list (car b))) (cdr b)))))
任何帮助将不胜感激!
答案 0 :(得分:5)
由于这是你的第一个家庭作业,而且你是Lisp的新手,这里有一个非常简单的自上而下的方法,不用担心性能,并且好好利用CL提供的工具:
在Common Lisp中,已经有一个删除重复项的函数:remove-duplicates
。将它与:from-end
关键字参数一起使用将“保留顺序”。现在,假设您有一个函数flatten
,它可以展开任意嵌套列表。然后问题的解决方案是:
(defun new-union (list1 list2)
(remove-duplicates (flatten (list list1 list2)) :from-end t))
这是我在没有给出进一步限制的情况下解决问题的方法,并且没有真正的理由担心性能问题。尽可能多地使用当前工具箱,除非必要,否则不要重新发明轮子。
如果你像这样处理问题,可以归结为编写flatten
函数,我将把它作为练习留给你。这不是太难,一个简单的选择是写一个递归函数,接近这样的问题:
如果要展平的列表的第一个元素本身就是一个列表,则将展平的第一个元素追加到展平的休息符。如果第一个元素不是列表,只需将其添加到列表的展平其余部分。如果输入根本不是列表,则返回它。
这应该是一个很好的练习,只需几行代码即可完成。
(如果你想要非常正确,可以使用辅助函数来完成工作并检查包装函数是否参数确实是一个列表。否则,flatten
也会对原子起作用,这可能是或者对你来说可能不是问题。)
现在,假设您已撰写flatten
:
> (defun new-union (list1 list2)
(remove-duplicates (flatten (list list1 list2)) :from-end t))
NEW-UNION
> (new-union 'a 'b)
(A B)
> (new-union 'a '(b))
(A B)
> (new-union '(a b) '(b c))
(A B C)
> (new-union '(((a))) '(b (c ((d e)) a)))
(A B C D E)
答案 1 :(得分:3)
解决此问题的一种方法是分离您的疑虑。一个是扁平化;另一个是重复删除;另一个是结果建设。
从空列表开始作为结果,继续向其中添加第一个列表的元素,跳过结果中已有的元素。
然后对第二个列表的元素执行相同操作,将它们添加到相同的结果列表中。
(defun my-union (a b &aux (res (list 1)) (p res))
(nadd-elts p a)
(nadd-elts p b)
(cdr res))
nadd-elts
将添加到列表的末尾,使用例如p
破坏性地更新其最后一个单元格(由rplacd
指向) nadd-elts
。一个例子是here。
要添加元素,p
会emulate flattening过程,并在检查res
重复项后,将每个叶元素添加到(defun my-union (a b &aux res)
(setq res (add-into res a))
(setq res (add-into res b))
res)
。
在功能样式中工作,没有破坏性更新,一般方法保持不变:从空结果列表开始,将第一个列表添加到其中 - 没有重复 - 然后是第二个。
add-into
现在我们继续实施(defun add-into (res a &aux r1 r2)
(cond
((atom a) .... )
(T (setq r1 (add-into res (car a)))
(setq r2 (............ (cdr a)))
r2)))
功能。
set
上述内容可以在没有辅助变量且没有(defun my-union (a b) (add-into NIL (cons a b)))
(defun add-into (res a)
(cond
((atom a) .... )
(T (add-into (add-into res (car a))
(cdr a)))))
原语的情况下重写。试着找出......好吧,这就是我的意思:
{{1}}
答案 2 :(得分:2)
除非你不允许使用哈希表(出于某种原因我之前已经遇到过这个问题),你可以提出一个排序函数来帮助你构建结果集,即你不必一遍又一遍地重复搜索。
此外,由于允许嵌套列表,因此您的问题可以缩小到仅删除树中的重复项(因为您可以在开始处理之前简单地追加任意数量的列表。
现在,我将尝试展示一些如何做到的例子:
;; Large difference between best and worst case.
;; Lists containing all the same items will be processed
;; in square time
(defun union-naive (list &rest lists)
(when lists (setf list (append list lists)))
(let (result)
(labels ((%union-naive (tree)
(if (consp tree)
(progn
(%union-naive (car tree))
(when (cdr tree) (%union-naive (cdr tree))))
(unless (member tree result)
(setq result (cons tree result))))))
(%union-naive list) result)))
;; Perhaps the best solution, it is practically linear time
(defun union-hash (list &rest lists)
(when lists (setf list (append list lists)))
(let ((hash (make-hash-table)) result)
(labels ((%union-hash (tree)
(if (consp tree)
(progn
(%union-hash (car tree))
(when (cdr tree) (%union-hash (cdr tree))))
(setf (gethash tree hash) t))))
(%union-hash list))
(maphash
#'(lambda (a b)
(declare (ignore b))
(push a result)) hash)
result))
;; This will do the job in more time, then the
;; solution with the hash-map, but it requires
;; significantly less memory. Memory is, in fact
;; a more precious resource some times, but you
;; have to decide what algo to use based on the
;; data size
(defun union-flatten (list &rest lists)
(when lists (setf list (append list lists)))
(labels ((%flatten (tree)
(if (consp tree)
(if (cdr tree)
(nconc (%flatten (car tree))
(%flatten (cdr tree)))
(%flatten (car tree)))
(list tree))))
;; the code below is trying to do something
;; that you could've done using
;; (remove-duplicates (%flatten list))
;; however sorting and then removing duplicates
;; may prove to be more efficient
(reduce
#'(lambda (a b)
(cond
((atom a) (list a))
((eql (car a) b) b)
(t (cons b a))))
(sort (%flatten list)
#'(lambda (a b)
(string< (symbol-name a)
(symbol-name b)))))))
(union-naive '(((a))) '(b(c((d e))a)))
(union-hash '(((a))) '(b(c((d e))a)))
(union-flatten '(((a))) '(b(c((d e))a)))
请注意,我用于订购元素的函数不是通用的,但您可能能够为任何类型的数据提供替代函数。一般来说,任何快速散列函数都会这样做,为了简单起见,我使用了这个函数。