7

Simple question, given a list like this

Clear[a, b, c, d, e, f];
lst = {{a, b}, {c, d}, {e, f}};

and suppose I have a function defined like this:

foo[x_,y_]:=Module[{},...]

And I want to apply this function to the list, so If I type

Map[foo, lst]

This gives

{foo[{a, b}], foo[{c, d}], foo[{e, f}]}

I want it to come out as

{foo[a, b], foo[c, d], foo[e, f]}

so it works.

What is the best way to do this? Assume I can't modify the function foo[] definition (say it is build-in)

Only 2 ways I know now are

Map[foo[#[[1]], #[[2]]] &, lst]
{foo[a, b], foo[c, d], foo[e, f]}

(too much work), or

MapThread[foo, Transpose[lst]]
{foo[a, b], foo[c, d], foo[e, f]}

(less typing, but need to transpose first)

Question: Any other better ways to do the above? I looked at other Map and its friends, and I could not see a function to do it more directly than what I have.

Nakilon
  • 34,866
  • 14
  • 107
  • 142
Nasser
  • 12,849
  • 6
  • 52
  • 104
  • 1
    [Related](http://stackoverflow.com/q/5746717/499167) question: **Apply list to arguments in Mathematica** – 681234 Jan 07 '12 at 00:43

5 Answers5

14

You need Apply at Level 1 or its short form, @@@

foo@@@lst    
{foo[a, b], foo[c, d], foo[e, f]}
abcd
  • 41,765
  • 7
  • 81
  • 98
7

One possible way is to change head of each element of lst from List to foo:

foo @@ # & /@ lst
{foo[a, b], foo[c, d], foo[e, f]}
Andrei
  • 55,890
  • 9
  • 87
  • 108
6

Just to report puzzling performance tests of the both methods (@@@, @@ # & /@) :

        T = RandomReal[{1,100}, {1000000, 2}];

        H[F_Symbol, T_List] := 

                     First@AbsoluteTiming[F @@@ T;]/First@AbsoluteTiming[F @@ # & /@ T;]

        Table[{ToString[F], H[F, T]},  {F, {Plus, Subtract, Times, Divide, Power, Log}}]

Out[3]= {{"Plus",     4.174757}, 
         {"Subtract", 0.2596154}, 
         {"Times",    3.928230}, 
         {"Divide",   0.2674164}, 
         {"Power",    0.3148629},
         {"Log",      0.2986936}}

These results are not random, but roughly proportional for very different data sizes.

@@@ is roughly 3-4 times faster for Subtract, Divide, Power, Log while @@ # & /@ is 4 times faster for Plus and Times giving rise to another questions, which (as one can believe) could be slightly
clarified by the following evaluation:

 Attributes@{Plus, Subtract, Times, Divide, Power, Log}

Only Plus and Times have attributes Flat and Orderless, while among the rest only Power (which seems relatively the most efficient there) has also an attribute OneIdentity.

Edit

A reliable explanation to observed performance boosts (thanks to Leonid Shifrin's remarks) should go along a different route.

By default there is MapCompileLength -> 100 as we can check evaluating SystemOptions["CompileOptions"]. To reset autocompilation of Map we can evaluate :

SetSystemOptions["CompileOptions" -> "MapCompileLength" -> Infinity]

Now we can test relative performance of the both methods by evaluating once more our H - performance testing function on related symbols and list :

          Table[{ToString[F], H[F, T]}, {F, {Plus, Subtract, Times, Divide, Power, Log}}]

 Out[15]= {{"Plus",      0.2898246},
           {"Subtract",  0.2979452}, 
           {"Times",     0.2721893}, 
           {"Divide",    0.3078512}, 
           {"Power",     0.3321622},
           {"Log",       0.3258972}}

Having these result we can conclude that in general Yoda's approach (@@@) is the most efficient, while that provided by Andrei is better in case of Plus and Times due to automatic compilation of Map allowing better performance of (@@ # & /@).

Artes
  • 1,133
  • 1
  • 17
  • 24
  • 3
    Not so puzzling if we recall that `Map` autocompiles when it can, and `Apply` can be compiled for just 3 heads: `Plus`, `Times` and `List`. OTOH, `@@@` does not autocompile. You see efficiency boosts for `Plus` and `Times` due to autocompilation in the `@@#&/@` construct, and because your input is a large packed array (which allows one to benefit from autocompilation) – Leonid Shifrin Jan 07 '12 at 06:38
  • 1
    See also this answer of mine: http://stackoverflow.com/questions/6405304/memory-use-of-apply-vs-map-virtual-memory-use-and-lock-ups/6408489#6408489, and the comments below it, for more discussion of similar matters. – Leonid Shifrin Jan 07 '12 at 13:00
  • @Leonid Thank You for interesting link and comments. Indeed, when I evaluate my `H` function on `T1 = FromPackedArray[T]` relative efficiency of `Plus` and `Times` slows down roughly by the factor 2, while the other functions only by a few percents, however `Map` is still almost two times faster for `Plus` and `Times`. The reason of this effect is apparently autocompilation. On the other hand, remarks on Attributes of related functions are still valid and I hope there shouldn't be any kind of misunderstanding. – Artes Jan 07 '12 at 19:49
  • 1
    Frankly, I think that autocompilation is the *sole* reason for the performance difference, while other attributes you mention don't contribute to it in this particular case. To see that, execute `SetSystemOptions["CompileOptions" -> "MapCompileLength" -> Infinity]` - this effectively disables auto-compilation. Then redo your timings and you will see that `Times` and `Plus` now behave the same as the rest (make sure to restore "MapCompileLength" to its default (100)).To be sure, attributes can make a difference, but not in this case I guess. – Leonid Shifrin Jan 07 '12 at 20:33
  • Thank You for helpful comments. What to do in case one would like to set permanently different MapCompileLength, ...Option Inspector ? I guess this is not recommended. Have you tried to do it ? – Artes Jan 08 '12 at 07:29
  • To downvoter, Could you be more specific what is wrong in my answer, or what can be improved ? – Artes Jan 08 '12 at 11:00
  • I actually never tried to change that option globally. One thing you can do is to put the `SetSystemOptions["CompileOptions" -> "MapCompileLength" -> your value]` line in your `init.m` (the one for the kernel). But, why bother? The default seems very sensible, and why would you want to change it? The compilation is to byte-code, and does not take that much time, while generally improves code efficiency. – Leonid Shifrin Jan 08 '12 at 11:59
  • It is not my intention to change default values of SystemOptions, but I wondered what to do in case of a need. Conditionally, as one could deduce from my post, it would be helpful in certain serious computational tasks. – Artes Jan 08 '12 at 13:07
  • The only route to change these I am aware of is through `SetSystemOptions`. May be there are other ways. – Leonid Shifrin Jan 08 '12 at 13:28
4

A few more possibilities to pick from:

This one is a more verbose version of yoda's answer. It applies foo at level 1 of the list lst only (replaces the head List with the head foo):

Apply[foo, lst, {1}]

This does the same, but maps Apply over the list lst (essentially Andrei's answer):

Map[Apply[foo, #] &, lst ]

And this just replaces the pattern List[x__] with foo[x] at level 1:

Replace[lst, List[x__] -> foo[x], 1]
Arnoud Buzing
  • 15,383
  • 3
  • 20
  • 50
3

The answers on Apply[] are spot on, and is the right thing to do, but what you were trying to do, was to replace a List[] head with a Sequence[] head, i.e. List[List[3,5],List[6,7]] should become List[Sequence[3,5],Sequence[6,7]].

Sequence head is what naturally remains if a head of any list of parameters is deleted, so Delete[Plus[3,5],0] and Delete[{3,5},0] and Delete[List[3,5],0] would all produce Sequence[3,5].

So foo@Delete[#,0]&/@{{a, b}, {c, d}, {e, f}} will give you the same as foo@@@{{a, b}, {c, d}, {e, f}}.

Alternatively, foo[#/.List->Sequence]&/@{{a, b}, {c, d}, {e, f}} does the same thing.

Gregory Klopper
  • 2,285
  • 1
  • 14
  • 14
  • 1
    I have to disagree with the statement that he wants to turn `List[List[...] ..]` into `List[Sequence[...] ..]`. More correctly, he wants `List[f[...] ..]`, i.e. he wants to change the heads of the inner lists to `f`. – rcollyer Jan 07 '12 at 02:21
  • I agree that this is what he ultimately "wants". I meant to say it in a sense "what you want to do to get there is..." ;-) Sorry for the confusion. To get to `List[f[Sequence[...]], ...]` he needed a way to convert a list of lists to a list of sequences. Apply does it internally. – Gregory Klopper Jan 07 '12 at 03:53