[英]Lisp function: union
我有一个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)))))
任何帮助,将不胜感激!
由于这是你的第一个家庭作业,而且你是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)
解决这个问题的一种方法是分开您的顾虑。 一个是扁平化; 另一个是重复删除; 另一个是结果建设。
从空列表开始作为结果,继续向其中添加第一个列表的元素,跳过结果中已有的元素。
然后对第二个列表的元素执行相同操作,将它们添加到相同的结果列表中。
(defun my-union (a b &aux (res (list 1)) (p res))
(nadd-elts p a)
(nadd-elts p b)
(cdr res))
nadd-elts
将添加到列表的末尾,使用例如rplacd
破坏性地更新其最后一个单元格(由p
指向)。 一个例子是在这里 。
要添加元素, nadd-elts
将模拟展 平过程,并在检查res
重复项后将每个叶元素添加到p
。
在功能样式中工作,没有破坏性更新,一般方法保持不变:从空结果列表开始,将第一个列表添加到其中 - 没有重复 - 然后是第二个。
(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)))))
除非你不被允许使用哈希表(出于某种原因我之前已经遇到过这个问题),你可以提出一个排序函数来帮助你构建结果集,就像你没有一遍又一遍地重复搜索。
此外,由于允许嵌套列表,您的问题可以缩小到仅删除树中的重复项(因为您可以在开始处理之前简单地追加任意数量的列表。
现在,我将尝试展示一些如何做到这一点的例子:
;; 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)))
请注意,我用于命令元素的函数不是通用的,但您可能能够为任何类型的数据提供替代函数。 一般来说,任何快速散列函数都可以,我为了简单起见使用了这个函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.