如何在lisp中生成一个笛卡尔积?

时间:2018-04-17 04:04:11

标签: loops lisp common-lisp generator cartesian-product

这是我生成笛卡尔积的代码:

(defun cartesian-product (LIST)
  (LOOP FOR X IN LIST
    NCONC
        (LOOP FOR Y IN LIST
         COLLECT (LIST X Y))))

我尝试输出其中一个笛卡儿产品:

(defun cartesian-product-generator (CALLBACK LIST)
  (LOOP FOR X IN LIST
    NCONC
        (LOOP FOR Y IN LIST
        DO(FUNCALL CALLBACK (LIST X Y)))))

但是当我尝试使用以下测试时出现错误:

(cartesian-product-generator '(A B C))

Error: Too few arguments in call to #<Compiled-function cartesian-product-generator #x30200097E60F>:
       1 argument provided, at least 2 required.  While executing: cartesian-product-generator, in process listener(1).

我是LISP的新手,想知道为什么会出现错误以及如何解决此错误。最终,我希望每次调用函数输出每个笛卡尔积。

例如,如果列表包含((1 1) (1 2) (2 1) (2 2))。 我想生成(1 1)。然后是(1 2)。然后是(2 1)。最后,(2 2)

2 个答案:

答案 0 :(得分:5)

您的第一个代码可以正常运行。

(defun cartesian-product (list)
  (loop
    for x in list
    nconc (loop for y in list
                collect (list x y))))

使用'(a b c)调用它会返回一个列表:

((A A) (A B) (A C) (B A) (B B) (B C) (C A) (C B) (C C))

您希望避免构建列表并改为使用回调。 为简化起见,首先尝试仅打印元素而不是收集它们。

这意味着你不关心关于返回生成的值 直到调用者:你只想生成它们并将它们打印出来 他们有空的时候。

基本上,您可以按nconc替换所有collectdo个关键字,并添加对print的调用:

(defun cartesian-product (list)
  (loop
    for x in list
    do (loop for y in list
             do (print (list x y)))))

使用'(a b c)对REPL进行快速测试时应该打印相同的内容 以前的元素,每个元素都在一个separte行。

现在,您可以概括print并调用您想要的任何内容:

(defun map-cartesian-product (function list)
  (loop
    for x in list
    do (loop for y in list
             do (funcall function (list x y)))))

只是看它是否仍然有效,快速测试一下:

(map-cartesian-product #'print '(a b c))

这应该具有与以前相同的行为。

由于您只遍历列表中的副作用,因此您可以使用 DOLIST

(defun map-cartesian-product (function list)
  (dolist (x list)
    (dolist (y list)
      (funcall function (list x y)))))

同样,你可以测试它仍然像以前一样工作。

答案 1 :(得分:2)

您可能希望通过cl-coroutine查看可用的quicklisp库。然后您的cartesian-product可以写成

(cl-coroutine:defcoroutine cartesian-product (list)
  (loop for x in list
    do (loop for y in list
      do (cl-coroutine:yield (list x y)))))

示例用法可以是:

(cl-coroutine:with-coroutine (cartesian-product)
  (let ((x (list 1 2 3)))
    (loop for y = (cartesian-product x)
      while y do (print y))))

; outputs (1 1) (1 2) ... (3 3)