13
$\begingroup$

Say I have two lists,

list1 = {a, b, c}
list2 = {x, y, z}

and I want to map a function f over them to produce

{f[a,x], f[a,y], f[a,z], f[b,x], f[b,y], f[b,z], f[c,x], f[c,y], f[c,d]}

I would assume I map the function over the first list to produce a "list of functions", which then run over the 2nd list, something like:

Map[Map[f[#1, #2]&,list1]&, list2]

but I can't figure out how to leave #2 "empty" until the 2nd map kicks in. How can I separate them to generate all combinations of arguments?

$\endgroup$
1
  • 2
    $\begingroup$ I can't figure out how to leave #2 "empty": You might be able to get away with FunctionMap[Function[x, Map[f[#1, x] &, list1]], list2] — although I'd use Outer myself. $\endgroup$
    – rm -rf
    Commented Nov 30, 2012 at 22:29

3 Answers 3

18
$\begingroup$

What you try to achieve here is called Currying which can be used in other languages like Haskell naturally. In Mathematica this does not work like that.

But what about

Outer[f, list1, list2]
(*
  {{f[a, x], f[a, y], f[a, z]}, 
   {f[b, x], f[b, y], f[b, z]}, 
   {f[c, x], f[c, y], f[c, z]}}
*)

or Flatten@Outer[f, list1, list2] if you want a flat list?

Of course this did not answer your question. Therefore, the real answer is: you can separate the Slots by using Function explicitely:

Map[Function[p2, Map[Function[p1, f[p1, p2]], list1]], list2]
(*
  {{f[a, x], f[b, x], f[c, x]}, 
   {f[a, y], f[b, y], f[c, y]}, 
   {f[a, z], f[b, z], f[c, z]}}
*)

Here, it is clear that the p1 parameter is for the inner Function, while p2 is for the outer one. But note, that for your ordering, you need to do switch parameters.

$\endgroup$
1
  • $\begingroup$ Perfect! Learned a lot, and solved the problem! Thank you very much! $\endgroup$
    – rayhem
    Commented Nov 30, 2012 at 22:39
7
$\begingroup$

Distribute is also handy.

Assuming f is not Listable:

In[39]:= Distribute[f[{a, b, c}, {x, y, z}], List]

Out[39]= {f[a, x], f[a, y], f[a, z], f[b, x], f[b, y], f[b, z], 
 f[c, x], f[c, y], f[c, z]}
$\endgroup$
6
  • $\begingroup$ This outputs {{f[a, x], f[b, y], f[c, z]}}. Apply[f,Distribute[{{a, b, c}, {x, y, z}}, List],1] gives the result above $\endgroup$
    – tchronis
    Commented Dec 4, 2013 at 14:34
  • $\begingroup$ I do get the right result with the input Distribute[f[{a, b, c}, {x, y, z}], List] $\endgroup$ Commented Dec 4, 2013 at 22:04
  • $\begingroup$ Yes you are right, probably I had earlier applied some rules that affected the result. I cannot reproduce them - I will try on my other machine also tomorrow. Sorry for the inconvenience. $\endgroup$
    – tchronis
    Commented Dec 4, 2013 at 22:09
  • $\begingroup$ Not at all. But interesting, I wonder what setting would give you that result. $\endgroup$ Commented Dec 4, 2013 at 22:15
  • $\begingroup$ I found the usual suspect. I had set f's Attributes to Listable. So maybe this should be noticed... $\endgroup$
    – tchronis
    Commented Dec 4, 2013 at 22:32
6
$\begingroup$

Or you could use Tuples, which appears a bit more natural to me.

Tuples[{{a, b, c}, {x, y, z}}]

creates

{{a, x}, {a, y}, {a, z}, {b, x}, {b, y}, {b, z}, {c, x}, {c, y}, {c, z}}

Afterwards Apply can be used to apply your function to the sublist

Apply[f , Tuples[{{a, b, c}, {x, y, z}}], {1}]

creates:

{f[a, x], f[a, y], f[a, z], f[b, x], f[b, y], f[b, z], f[c, x], f[c, y], f[c, z]}

Addition: comparison of speed:

create some random data:

list1 = RandomReal[1, 10^3];
list2 = RandomReal[1, 10^3];

Usage of a pure function to summarize the arguments (#1 + #2) &

 Apply[(#1 + #2) &, Tuples[{list1, list2}], {1}]; // AbsoluteTiming

yields {0.944316, Null}

Outer[(#1 + #2) &, list1, list2]; // AbsoluteTiming

yields {0.506706, Null}


$\endgroup$
3
  • $\begingroup$ I didn't know about tuples, but I had come to the same sort of solution (lists as arguments) using shifts and transposes. Apply[f, <list things>, 1] worked great to turn the lists into actual arguments. Thanks! $\endgroup$
    – rayhem
    Commented Nov 30, 2012 at 22:39
  • 1
    $\begingroup$ Apply (@@@) would be a better choice here than Map $\endgroup$ Commented Nov 30, 2012 at 22:41
  • $\begingroup$ Of course Mike is right: Apply is what you were looking for. I missed to notice that you explicitly not wanted to end up with a list of variables as input argument. $\endgroup$
    – Sascha
    Commented Nov 30, 2012 at 22:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.