【问题标题】:Generate combinations生成组合
【发布时间】:2018-07-07 11:20:49
【问题描述】:

我正在尝试在 Lisp 中编写一个函数来生成给定键和值的所有可能组合。这是一个输入和输出示例:

Input: '((key1 . (v1 v2))
         (key2 . (v3 v4)))

Output: '(((key1 . v1)(key2 . v3))
          ((key1 . v1)(key2 . v4))
          ((key1 . v2)(key2 . v3))
          ((key1 . v2)(key2 . v4)))

目前,我的功能如下:

(defun generate-selectors (selectors)
  (cond ((= (length selectors) 0) nil)
        ((= (length selectors) 1)
         (let* ((keys (mapcar #'first selectors))
                (key (first keys))
                (values (rest (assoc key selectors))))
           (loop for val in values
                 collect (cons key val))))
        (t
         (let* ((keys (mapcar #'first selectors))
                (key (first keys))
                (values (rest (assoc key selectors)))
                (rest (remove (assoc key selectors) selectors)))
            (loop for r in (generate-selectors rest)
                  append (loop for val in values
                               collect (cons (cons key val) (list r))))))))

对于上面给出的输入,函数按预期工作:

> (generate-selectors '((key1 . (v1 v2 v3)) (key2 . (v4 v5))))
  (((KEY1 . V1) (KEY2 . V4))
   ((KEY1 . V2) (KEY2 . V4))
   ((KEY1 . V3) (KEY2 . V4))
   ((KEY1 . V1) (KEY2 . V5))
   ((KEY1 . V2) (KEY2 . V5))
   ((KEY1 . V3) (KEY2 . V5)))

但是,对于更长的输入,输出不再正确!

> (generate-selectors '((key1 . (v1 v2 v3)) (key2 . (v4 v5)) (key3 . (v6))))
  (((KEY1 . V1) ((KEY2 . V4) (KEY3 . V6)))
   ((KEY1 . V2) ((KEY2 . V4) (KEY3 . V6)))
   ((KEY1 . V3) ((KEY2 . V4) (KEY3 . V6)))
   ((KEY1 . V1) ((KEY2 . V5) (KEY3 . V6)))
   ((KEY1 . V2) ((KEY2 . V5) (KEY3 . V6)))
   ((KEY1 . V3) ((KEY2 . V5) (KEY3 . V6))))

请注意,在上面的输出中,KEY2KEY3 嵌套在另一个子列表中。正确的输出应该是这样的:

(((KEY1 . V1) (KEY2 . V4) (KEY3 . V6))
 ((KEY1 . V2) (KEY2 . V4) (KEY3 . V6))
 ...                                  )

是什么导致我的generate-selectors 函数出现这种情况?

编辑:当不在列表中包装 r 时,我得到以下输出:

> (generate-selectors '((key1 . (v1 v2 v3)) (key2 . (v4 v5)) (key3 . (v6))))
  (((KEY1 . V1) (KEY2 . V4) KEY3 . V6)
   ((KEY1 . V2) (KEY2 . V4) KEY3 . V6)
   ((KEY1 . V3) (KEY2 . V4) KEY3 . V6)
   ((KEY1 . V1) (KEY2 . V5) KEY3 . V6)
   ((KEY1 . V2) (KEY2 . V5) KEY3 . V6)
   ((KEY1 . V3) (KEY2 . V5) KEY3 . V6))

【问题讨论】:

  • 一般来说,我建议使用@coredump 中的 COMBINATIONS 函数之类的东西,然后创建它的输入。键的处理似乎不必要且复杂 - 最好使用专门的函数来计算组合 -> 更好地调试,

标签: list loops lisp common-lisp combinations


【解决方案1】:

鉴于之前的解决方案是正确的,我想提出一个替代解决方案。给定列表 A1, A2, ... An 的列表,以下函数执行它们的笛卡尔积 (A1 x A2 x ... x An):

(defun cartesian-product (l)
  (if (null l)
      (list nil)
      (loop for x in (car l) 
            nconc (loop for y in (cartesian-product (cdr l)) collect (cons x y)))))

那么函数generate-selectors可以定义为:

(defun generate-selectors (selectors)
  (cartesian-product (loop for s in selectors
                       collect (loop for val in (cdr s) collect (cons (car s) val)))))

【讨论】:

    【解决方案2】:

    这里:

    (cons (cons key val) (list r))
    

    R 是递归获得的,是一个列表。您将其包装在列表中。试试吧:

    (cons (cons key val) r)
    

    此外,在一般情况下,当您调用append 时,您希望得到一个列表列表。但是,您的基本案例不会生成列表列表,而只会生成列表。 您需要将额外的list 放在cons 周围的基本情况中:

    (loop for val in values
          collect (list (cons key val)))
    

    另一个版本

    如果你不需要钥匙,这个稍微简单一点。根据 Renzo 的回答,我(重新)将函数命名为 product,因为您正在做的事情被称为 Cartesian product

    (defun product (lists)
      (if lists
          (destructuring-bind (head . lists) lists
            (loop
              with product = (product lists) 
              for value in head
              append (loop
                       for tuple in product
                       collect (cons value tuple))))
          (list (list))))
    
    (product '((a b) (0 1 2)))
    => ((A 0) (A 1) (A 2) (B 0) (B 1) (B 2))
    

    【讨论】:

    • 这也不会产生正确的输出。我将编辑我的问题。
    • 啊!那是缺少的链接!
    • 但是!!这再次打破了这种情况的输出:(generate-selectors '((key1 . (v1 v2 v3)) (key2 . (v4 v5))))
    • @JNevens 现在看起来好多了
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-26
    • 2011-09-14
    相关资源
    最近更新 更多