Can be this lisp function be implemented recursive

2019-06-10 18:30发布

The goal of this function is to generate the cartesian product of 2 list. For example

  • (combo (list 1 2 3) (list 4 5)) => (1 4) (1 5) (2 4) (2 5) (3 4) (3 5)

    (defun combo (l1 l2)
    (let    (
                (res (list))
            )
        (dolist (elem1 l1 res)
            (dolist (elem2 l2 res)
                (setq res (append res (list(list elem1 elem2))))
            )
        )
    )
    

    )

How could be implemented recursively?

2条回答
走好不送
2楼-- · 2019-06-10 19:18

A simple recursive implementation would be to use two helper functions; one to traverse through L1 (%COMBO in the code below), calling another function to pair an element with each element from L2 (%PRODUCT):

(defun combo (l1 l2)
  (labels ((%product (el list &optional (acc (list)))
             (if (endp list)
                 acc
                 (%product el (rest list) (cons (list el (first list)) acc))))
           (%combo (l1 l2 &optional (acc (list)))
             (if (endp l1)
                 (nreverse acc)
                 (%combo (rest l1) l2 (nconc (%product (first l1) l2) acc)))))
    (%combo l1 l2)))

The iterative approach is both simpler and more efficient though. Instead of using APPEND in a loop, you should just reverse the list at the end.

(defun combo (l1 l2)
  (let ((res (list)))
    (dolist (e1 l1 (nreverse res))
      (dolist (e2 l2)
        (push (list e1 e2) res)))))

You could also just use the MAP-PRODUCT function from Alexandria:

CL-USER> (ql:quickload :alexandria)
;=> (:ALEXANDRIA)
CL-USER> (use-package :alexandria)
;=> T
CL-USER> (map-product #'list (list 1 2 3) (list 4 5))
;=> ((1 4) (1 5) (2 4) (2 5) (3 4) (3 5))
查看更多
霸刀☆藐视天下
3楼-- · 2019-06-10 19:18

It seems ideal for Prolog, though data has to be put differently:

items1(1).
items1(2).
items1(3).

items2(4).
items2(5).

?- findall((A,B), (items1(A), items2(B)), L).
L = [ (1, 4), (1, 5), (2, 4), (2, 5), (3, 4), (3, 5)].
查看更多
登录 后发表回答