使用临时结果时将列表项添加在一起

Adding List items together while using provisional result

我有以下列表:

((a b) (c d) (e f))

并想遍历它并将列表元素组合在一起。我也有循环,它的工作方式与我想要的完全一样:

(loop for (x . tail) on l1
      append (loop for y in tail
                   collect (append (list '&)  x y)))

问题是,我想先添加前两个元素,

((a b) (c d)) -> ((& a b ) (& c d) (& a b c d))

并使用此临时结果添加下一个列表 (e f)

((a b ) (c d) (a b c d) (e f)) -> ((& a b e f) (& c d e f) (& a b c d e f))

我是否必须编写一个额外的函数,首先使用两个元素调用 lop,或者是否有一种循环变量来使用临时结果?我试过使用额外的功能,但它看起来太罗嗦和不自然了。我对 Lisp Loops 很陌生

问题陈述

OP 要求不明确。给定一个输入列表,并不完全清楚输出应该是什么; OP 在问题和评论中提供的示例输出中的不一致使情况更加复杂。

OP 从组合输入列表元素的循环开始:

(loop for (x . tail) on l1
      append (loop for y in tail
                   collect (append (list '&)  x y)))

此循环不会生成 OP 预期输出中所示的排序对:

((a b) (c d)) -> ((& a b ) (& c d) (& a b c d))

相反,上面的输出是:

((a b) (c d)) -> ((& a b c d))

((a b) (c d) (e f)) 进行操作的最终结果似乎是 ((& a b e f) (& c d e f) (& a b c d e f))。但这似乎缺少列表 (& a b c d).

在评论中出现了对列表((a b) (c d) (e f) (g h))进行操作的结果应该是((& a b) (& c d) (& a b c d) (& a b e f) (& c d e f) (& a b c d e f) (& a b g h) (& c d g h) (& a b c d g h) (& a b e f g h) (& c d e f g h) (& a b c d e f g h))。此结果包括列表 (& a b)(& c d),它们似乎与原始操作没有任何联系。此外,(& e f)(& g h) 神秘地没有出现在这个结果中。

不过,从这里看来,OP 似乎希望收集一个结果,其中包含第一个操作生成的所有原始组合以及对一系列列表进行操作的结果 cdr 和通过连接每个列表的第一对进行转换。

可能的解决方案

我并不是说这是一个最优的解决方案,一个特别好的解决方案,或者它甚至解决了 OP 的问题,因为它在这里没有很清楚地列出。我只声称这可能是 a 解决方案,它使用了 OP 的原始组合操作并且相当清楚。我怀疑从头开始重新思考问题可能会找到更好的解决方案。

OP操作可以放在一个函数中:

(defun combiner (xss)
  (loop for (x . tail) on xss
        append (loop for y in tail
                     collect (append x y))))

这里添加的符号'&被忽略;这只会在我们组合列表元素时混淆事情,并且在我们完成后可以很容易地将符号添加到结果列表之前。

无需使用 combiner 对列表的前两个元素进行操作,因为结果始终只是这两个列表元素的串联。编写一个通过连接前两个元素来转换列表的函数会很有用。单例列表应转换为 nil:

(defun fancy-transform (xss)
  (unless (null (cdr xss))
    (cons (append (first xss) (second xss))
          (cddr xss))))

现在我们准备编写一个使用 combiner 来创建所需结果的函数:

(defun fancy-combiner (xss)
  (mapcar (lambda (xs) (cons '& xs))
          (remove-duplicates
           (loop for uss on xss
                 append (loop for vss on uss by #'fancy-transform
                              append (combiner vss)))
           :test #'equal)))

操作在此处的 loop 宏中。由于循环的结果包含重复项,因此需要调用 remove-duplicates,并且调用 mapcar 用于将 '& 符号添加到结果中的每个列表。

外循环逐渐将输入列表的更小版本提供给内循环;这些列表中的每一个都被 combiner 转换,然后被 fancy-transform 减少,这通过附加前两个元素或在达到单例列表时返回 nil 来减少列表(结束该迭代) .

在处理输入列表时查看进度可能会有所帮助。此 fancy-combiner-illustrated 函数不会删除重复项或在结果列表中添加符号:

(defun fancy-combiner-illustrated (xss)
  (loop for uss on xss
        do (format t "~%USS: ~A" uss)
        append (loop for vss on uss by #'fancy-transform
                     do (format t "~%VSS: ~A" vss)
                     append (combiner vss))))
CL-USER> (fancy-combiner-illustrated '((a b) (c d) (e f) (g h)))

USS: ((A B) (C D) (E F) (G H))
VSS: ((A B) (C D) (E F) (G H))
VSS: ((A B C D) (E F) (G H))
VSS: ((A B C D E F) (G H))
VSS: ((A B C D E F G H))
USS: ((C D) (E F) (G H))
VSS: ((C D) (E F) (G H))
VSS: ((C D E F) (G H))
VSS: ((C D E F G H))
USS: ((E F) (G H))
VSS: ((E F) (G H))
VSS: ((E F G H))
USS: ((G H))
VSS: ((G H))
((A B C D) (A B E F) (A B G H) (C D E F) (C D G H) (E F G H) (A B C D E F)
 (A B C D G H) (E F G H) (A B C D E F G H) (C D E F) (C D G H) (E F G H)
 (C D E F G H) (E F G H))

列表 VSS 是由 combiner 操作的列表,并且由于其中许多列表的尾部相同,因此结果中出现重复项。在将 '& 符号添加到结果中的每个列表之前,fancy-combiner 函数使用 remove-duplicates 删除这些重复项。

结果

CL-USER> (fancy-combiner '((a b) (c d)))
((& A B C D))

CL-USER> (fancy-combiner '((a b) (c d) (e f)))
((& A B C D) (& A B E F) (& A B C D E F) (& C D E F))

CL-USER> (fancy-combiner '((a b) (c d) (e f) (g h)))
((& A B C D) (& A B E F) (& A B G H) (& A B C D E F) (& A B C D G H)
 (& A B C D E F G H) (& C D E F) (& C D G H) (& C D E F G H) (& E F G H))

这些不完全是 OP 在问题和评论中指出的结果,但由于 OP 评论中似乎存在不一致,因此很难确切知道预期的结果。

我需要编写一个额外的函数吗?

假设这个解决方案确实符合 OP 的期望,您 可以 通过使用 lambda 表达式代替 fancy-transform 并替换调用将整个事情写成一个函数使用等效的循环表达式到 combiner in fancy-combiner。这样做没有任何好处,而且生成的代码会不那么清晰。

OP 询问:“是否有一种循环变量可以使用临时结果?”,但似乎 OP 希望 更改 处理输入列表,例如,在输入遍历期间的某个时刻将 (a b c d) 插入输入列表。这在 Common Lisp 中是不可能的; it is forbidden to destructively modify any list during a traversal operation。循环工具不为此提供任何机制。

我在没有互联网的火车上开始解决这个问题。 所以当我到达时,另一个答案就在那里。 但是尽管如此,我还是会 post 我的构想。

((a b) (c d) (e f) (g h))
;; => ((a b) (c d) (e f) (g h)
;;     (a b c d) (a b e f) (c d e f) (a b g h) (c d g h)
;;     (a b c d e f) (a b c d g h) 
;;     (a b e f g h) 
;;     (c d e f g h) 
;;     (a b c d e f g h))

第一行是列表。 第二行是此列表中元素的配对 - 但仅与部分配对 cdr 最后一个元素。 before-last 行的第三行是与第二行和列表的 cdr 的配对。 最后一行是一切的统一。

(defun once-flatten (lol)
  (loop for x in lol
        if (atom x)
          collect x
        else
          append x))
        
(defun list-and-pairings* (list) ;; or #'append
  (append list
          (mapcar #'once-flatten
                  (loop for (x . tail) on list
                        append (loop for y in (if (> (length tail) 1)
                                                  (list-and-pairings* tail)
                                                  tail)
                                     collect (list x y))))))

(defun add-& (lol) (mapcar (lambda (x) (if (atom x) x (cons '& x))) lol))

然后我们可以做:

(add-& (list-and-pairings* '((a b) (c d) (e f) (g h))))
;; => ((& A B) (& C D) (& E F) (& G H) 
;;     (& A B C D) (& A B E F) (& A B G H)
;;     (& A B C D E F) (& A B C D G H) (& A B C D E F G H) (& A B E F G H)
;;     (& C D E F) (& C D G H) (& C D E F G H) (& E F G H))

也可以使用:

(defun add-& (lol)
 (loop for x in lol
       if (atom x)
         collect x
       else
         collect (cons '& x)))