From: Franz Kafka
Subject: Help Please (Permutations using Mapcar)
Date: 
Message-ID: <pyEqa.965$FH.324@news02.roc.ny.frontiernet.net>
I would like to take four lists:
'(A B)
'(D E)
'(G H)
'(J K)

and get the following output:

((A D G J) (A D G K) (A D H J) (A D H K)
 (A E G J) (A E G K)  (A E H J) (A E H K)
(B D G J) (B D G K) (B D H J) (B D H K)
(B E G J) (B E G K) (B E H J) (B E H K))

Are their any mapping operators that do this?

I want it to work for any number of symbols--I
just used two in my example so the message would be
short.

I use:

(mapcar #'first *list*)
to get the lists.

The lists look like
'((A 1 3) (B 2 5) .... (E 3 5))

Is there any mapping operator to do the permutations
I want to do--if not how would I do this.

From: Gareth McCaughan
Subject: Re: Help Please (Permutations using Mapcar)
Date: 
Message-ID: <slrnbamagl.22qt.Gareth.McCaughan@g.local>
"Franz Kafka" wrote:

> I would like to take four lists:
> '(A B)
> '(D E)
> '(G H)
> '(J K)
> 
> and get the following output:
> 
> ((A D G J) (A D G K) (A D H J) (A D H K)
>  (A E G J) (A E G K)  (A E H J) (A E H K)
> (B D G J) (B D G K) (B D H J) (B D H K)
> (B E G J) (B E G K) (B E H J) (B E H K))
>
> Is there any mapping operator to do the permutations
> I want to do--if not how would I do this.

There isn't anything provided in CL to do this.
The term you're looking for, by the way, is not
"permutations" but "Cartesian product".

    (defun product (lists)
      "Return a list built as follows. Each element contains one item
      from each of the LISTS, appearing in the same order as the LISTS
      themselves. There is one element for each way of doing this."
      (if lists
        (let ((subproduct (product (rest lists))))
          (loop for item in (first lists) nconc
            (mapcar (lambda (x) (cons item x)) subproduct)))
        (list nil)))

These lists can get quite long, in which case it
may be preferable not to generate the lists explicitly.

    (defun mapc-product (f lists)
      "Apply F to each list that can be obtained by choosing one
      element from each of the LISTS. Discard the result."
      (labels ((helper (lists tail)
                 (if lists
                   (loop for item in (first lists) do
                     (helper (rest lists) (cons item tail)))
                   (funcall f tail))))
        (helper (reverse lists) nil)))

Note that these generate the lists in different orders, which
may or may not matter to you. Alternatively you could do
this:

    (defmacro for-selections ((var lists) &body body)
      `(labels ((helper (lists ,var)
                  (if lists
                    (loop for item in (first lists) do
                      (helper (rest lists) (cons item ,var)))
                    (progn . ,body))))
         (helper (reverse ,lists) nil)))

though I'm not sure it has much advantage over the
functional version above. If you actually do this,
you should use gensyms instead of the hard-coded
names HELPER, LISTS and ITEM to avoid inadvertent
binding capture. In applications -- not yours, I think --
where the number of things whose product is being taken
is known at macroexpansion time, you can instead do
this (where, again, gensyms should be used instead
of the hard-coded names):

    (defmacro for-selections (specs &body body)
      (labels ((generate (specs)
                 (if specs
                   `(loop for ,(caar specs) in ,(cadar specs) do
                      ,(generate (cdr specs)))
                   `(progn . ,body))))
        (generate specs)))

The names I've given these things are doubtless not optimal.

-- 
Gareth McCaughan  ················@pobox.com
.sig under construc
From: Franz Kafka
Subject: Re: Help Please (Permutations using Mapcar)
Date: 
Message-ID: <9hZqa.1148$086.150@news02.roc.ny.frontiernet.net>
>
> There isn't anything provided in CL to do this.
> The term you're looking for, by the way, is not
> "permutations" but "Cartesian product".
>
>     (defun product (lists)
>       "Return a list built as follows. Each element contains one item
>       from each of the LISTS, appearing in the same order as the LISTS
>       themselves. There is one element for each way of doing this."
>       (if lists
>         (let ((subproduct (product (rest lists))))
>           (loop for item in (first lists) nconc
>             (mapcar (lambda (x) (cons item x)) subproduct)))
>         (list nil)))
>
> These lists can get quite long, in which case it
> may be preferable not to generate the lists explicitly.
>
>     (defun mapc-product (f lists)
>       "Apply F to each list that can be obtained by choosing one
>       element from each of the LISTS. Discard the result."
>       (labels ((helper (lists tail)
>                  (if lists
>                    (loop for item in (first lists) do
>                      (helper (rest lists) (cons item tail)))
>                    (funcall f tail))))
>         (helper (reverse lists) nil)))
>
> Note that these generate the lists in different orders, which
> may or may not matter to you. Alternatively you could do
> this:
>
>     (defmacro for-selections ((var lists) &body body)
>       `(labels ((helper (lists ,var)
>                   (if lists
>                     (loop for item in (first lists) do
>                       (helper (rest lists) (cons item ,var)))
>                     (progn . ,body))))
>          (helper (reverse ,lists) nil)))
>
> though I'm not sure it has much advantage over the
> functional version above. If you actually do this,
> you should use gensyms instead of the hard-coded
> names HELPER, LISTS and ITEM to avoid inadvertent
> binding capture.

>     (defmacro for-selections (specs &body body)
>       (labels ((generate (specs)
>                  (if specs
>                    `(loop for ,(caar specs) in ,(cadar specs) do
>                       ,(generate (cdr specs)))
>                    `(progn . ,body))))
>         (generate specs)))
>
> The names I've given these things are doubtless not optimal.
>

Thanks a mil. This helped.