Ex2.4 combine-allをcross-productで再定義

引数に指定された複数のリストから要素の全ての組み合わせを選択し、各組み合わせに対し、関数fnを呼び出した結果をリストとして返す関数cross-productを定義する。

その結果を使ってcombine-allを再定義する。

;;; Exercise 2.4 [m] One way of describing combine-all is that it calculates the cross-
;;; product of the function append on the argument-lists. Write the higher-order function
;;; cross-product, and define combine-all in terms of it.
;;; The moral is to make you code as general as possible, because you never know what
;;; you may want to do with it next.
(defun cross-product (fn &rest lists)
  (labels ((iter (lists args)
	     (if (null lists) (apply fn (reverse args))
		 (mapcar #'(lambda (x)
			      (iter (cdr lists) (cons x args)))
			  (first lists)))))
    (do ((result (iter lists '()) (apply #'append result))
	 (n 1 (1+ n)))
	((= n (length lists)) result))))

(cross-product #'list '(a b c)
	              '(1 2 3)
		      '(z x y))
;; => ((A 1 Z) (A 1 X) (A 1 Y) (A 2 Z) (A 2 X) (A 2 Y) (A 3 Z) (A 3 X) (A 3 Y)
;;    (B 1 Z) (B 1 X) (B 1 Y) (B 2 Z) (B 2 X) (B 2 Y) (B 3 Z) (B 3 X) (B 3 Y)
;;    (C 1 Z) (C 1 X) (C 1 Y) (C 2 Z) (C 2 X) (C 2 Y) (C 3 Z) (C 3 X) (C 3 Y))

(defun combine-all (xlist ylist)
  (cross-product #'append xlist ylist))

(combine-all '((a) (b)) '((1) (2)))
;; => ((A 1) (A 2) (B 1) (B 2))