Alexandria有一个函数map-product
,它接受任意数量的列表参数,并按顺序生成元素的所有组合,每个元素组合一个。例如:
(alexandria:map-product 'list '(1 2) '(3 4) '(5 6))
=> ((1 3 5) (1 3 6) (1 4 5) (1 4 6) (2 3 5) (2 3 6) (2 4 5) (2 4 6))
当参数中存在重复元素时,结果组合也将包含一些重复元素:
(alexandria:map-product 'list '(1 2) '(3 4) '(5 1))
=> ((1 3 5) (1 3 1) (1 4 5) (1 4 1) (2 3 5) (2 3 1) (2 4 5) (2 4 1))
其中(1 3 1)和(1 4 1)包含重复项。
我想从结果中删除包含重复项的所有此类列表。我目前的解决方案是简单地做:
(delete-if-not #'alexandria:setp result)
但这需要过多的后处理,特别是因为产生的组合数量通常为数百个。一个更好的解决方案是编写像map-product
这样的函数,它首先不会生成重复项。
zck在Lisp: How to get all possible combinations of the elements from lists contained on a list?的另一篇文章提供的函数大致相当于map-product
,似乎可以修改它以在内部删除重复项:
(defun combinations (&rest lists)
(if (car lists)
(mapcan (lambda (inner-val)
(mapcar (lambda (outer-val)
(cons outer-val
inner-val))
(car lists)))
(apply #'combinations (cdr lists)))
(list nil)))
然而,对我来说,如何插入重复测试并不明显。此外,一个简单的计时运行似乎表明这个函数比alexandria:map-product
慢大约16倍。获得此功能的更快版本是否可行,但没有重复组合?
答案 0 :(得分:1)
你可能需要检查一下这是否正确,但它应该给你一个想法:
CL-USER 40 > (defun combinations-1 (lists)
(if (car lists)
(mapcan (lambda (inner-val)
(mapcan (lambda (outer-val)
(unless (member outer-val inner-val)
(list (cons outer-val inner-val))))
(car lists)))
(combinations-1 (cdr lists)))
(list nil)))
COMBINATIONS-1
CL-USER 41 > (combinations-1 '((3 2) (1 2) (5 1)))
((3 1 5) (2 1 5) (3 2 5) (3 2 1))
另一个MAPCAN而不是MAPCAR过滤NIL。为此,我们需要返回列表,因此添加了LIST调用。我们只在列表中添加内容,如果它不是成员,否则使用空列表。
请注意,我还删除了& rest list / apply pattern。
问:所有重复的子列表是否都减少到NIL,以便通过MAPCAN删除它们?