首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何用lisp生成一个笛卡尔乘积?

如何用lisp生成一个笛卡尔乘积?
EN

Stack Overflow用户
提问于 2018-04-17 04:04:10
回答 4查看 1.4K关注 0票数 2

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

代码语言:javascript
运行
复制
(defun cartesian-product (LIST)
  (LOOP FOR X IN LIST
    NCONC
        (LOOP FOR Y IN LIST
         COLLECT (LIST X Y))))

我试着用这个输出笛卡尔的一种产品:

代码语言:javascript
运行
复制
(defun cartesian-product-generator (CALLBACK LIST)
  (LOOP FOR X IN LIST
    NCONC
        (LOOP FOR Y IN LIST
        DO(FUNCALL CALLBACK (LIST X Y)))))

但是,当我尝试用以下方法测试它时会出现错误:

代码语言:javascript
运行
复制
(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)

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-04-17 20:38:25

您的第一段代码确实正常工作。

代码语言:javascript
运行
复制
(defun cartesian-product (list)
  (loop
    for x in list
    nconc (loop for y in list
                collect (list x y))))

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

代码语言:javascript
运行
复制
((A A) (A B) (A C) (B A) (B B) (B C) (C A) (C B) (C C))

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

这意味着您不关心将生成的值返回给调用者:您只想生成它们并在它们可用时立即打印它们。

基本上,您可以用nconccollect关键字替换do,并添加对print的调用

代码语言:javascript
运行
复制
(defun cartesian-product (list)
  (loop
    for x in list
    do (loop for y in list
             do (print (list x y)))))

使用'(a b c)对REPL进行快速测试时,应该打印与以前相同的元素,每个元素都位于分隔线上。

现在,您可以简单地概括print并调用您想要的任何东西:

代码语言:javascript
运行
复制
(defun map-cartesian-product (function list)
  (loop
    for x in list
    do (loop for y in list
             do (funcall function (list x y)))))

为了看它是否仍然有效,做一个快速测试:

代码语言:javascript
运行
复制
(map-cartesian-product #'print '(a b c))

这样做的行为应该和以前一样。

因为您只对一个列表进行了副作用的迭代,所以可以使用DOLIST

代码语言:javascript
运行
复制
(defun map-cartesian-product (function list)
  (dolist (x list)
    (dolist (y list)
      (funcall function (list x y)))))

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

票数 7
EN

Stack Overflow用户

发布于 2018-04-17 17:15:24

您可能需要查看通过cl-coroutine提供的quicklisp库。那么您的cartesian-product可以编写为

代码语言:javascript
运行
复制
(cl-coroutine:defcoroutine cartesian-product (list)
  (loop for x in list
    do (loop for y in list
      do (cl-coroutine:yield (list x y)))))

一个例子可以是:

代码语言:javascript
运行
复制
(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)
票数 3
EN

Stack Overflow用户

发布于 2022-02-17 09:32:45

只想把我的两分钱留在这里。

您也可以使用宏来完成此操作。

代码语言:javascript
运行
复制
(defmacro cartesian-product (lists)
  (let* ((indices (loop for i from 1 to (length lists)
                        collect (gensym (format nil "~a-i-" i))))
         (initial-value `(loop for ,(car (last indices)) in ',(car (last lists))
                               collect `(,,@indices))))
    (reduce
     (lambda (x y)
       `(loop for ,(car x) in ',(cadr x)
              nconc ,y))
     (mapcar #'list (butlast indices) (butlast lists))
     :from-end t
     :initial-value initial-value)))

它会随着

代码语言:javascript
运行
复制
(cartesian-product ((H P) (a b c) (1 2 3 5)))

代码语言:javascript
运行
复制
(loop for #:|1-i-806| in '(h p)
      nconc (loop for #:|2-i-807| in '(a b c)
                  nconc (loop for #:|3-i-808| in '(1 2 3 5)
                              collect `(,#:|1-i-806| ,#:|2-i-807| ,#:|3-i-808|))))

和结果

代码语言:javascript
运行
复制
((H A 1) (H A 2) (H A 3) (H A 5) (H B 1) (H B 2) (H B 3) (H B 5) (H C 1) (H C 2) (H C 3) (H C 5)
 (P A 1) (P A 2) (P A 3) (P A 5) (P B 1) (P B 2) (P B 3) (P B 5) (P C 1) (P C 2) (P C 3) (P C 5))

我喜欢它,因为它很简单,不需要递归。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49869728

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档