12

I want to archive:

Map2[f,{a,b,c,d}]
{f[a,b], f[b,c], f[c,d]}

But for some reason, I can only think of iterative approaches.

What's the functional way?

Edit:

Use of this, was to generate graph examples. If number list is given, following generates the graph of what it would mean, to sequentially visit all the nodes.

For example:

Map2 = # @@@ Partition[#2, 2, 1] &;
MG[elems_] := Graph[Map2[DirectedEdge, elems]]
nrs = RandomInteger[{1, 15}, 20]
MG[nrs]

{10,13,9,7,13,3,5,1,15,10,15,6,14,3,1,2,11,4,8,5}

Radial layout:

enter image description here

Community
  • 1
  • 1
Margus
  • 19,694
  • 14
  • 55
  • 103
  • @d00b it must be; `Graph` is not in v7. – Mr.Wizard May 30 '11 at 11:03
  • 1
    @d00b: Yes, this use case is for Mathematica 8. Although note, that question itself is not version specific. All the more reason, to appreciate good answers. – Margus May 30 '11 at 20:33

6 Answers6

11

Most simply:

Map2 = # @@@ Partition[#2, 2, 1] &;

Or, possibly using less memory, but running a bit slower:

Map2[f_, lst_] := Developer`PartitionMap[f @@ # &, lst, 2, 1]

Also, be aware of Differences and Accumulate, which are related.


Other ways:

Inner[#, Most@#2, Rest@#2, List] &

Notice that in this one List could be a different function.

MapThread[#, {Most@#2, Rest@#2}] &

Most@MapThread[#, {#2, RotateLeft@#2}] &

Rest@MapThread[#, {RotateRight@#2, #2}] &

Table[# @@ #2[[i ;; i + 1]], {i, Length@#2 - 1}] &

Now, the timings. I will make use of Timo's timeAvg.

f1 = # @@@ Partition[#2, 2, 1] &;
f2[f_, lst_] := Developer`PartitionMap[f @@ # &, lst, 2, 1]
f3 = Inner[#, Most@#2, Rest@#2, List] &;
f4 = MapThread[#, {Most@#2, Rest@#2}] &;
f5 = Most@MapThread[#, {#2, RotateLeft@#2}] &;
f6 = Table[# @@ #2[[i ;; i + 1]], {i, Length@#2 - 1}] &;

timings = 
  Table[
   list = RandomReal[99, 10^i];
   func = Plus;
   timeAvg[#[func, list]] & /@ {f1, f2, f3, f4, f5, f6},
   {i, 6}
  ];

TableForm[timings, 
 TableHeadings -> {10^Range@6, {"f1", "f2", "f3", "f4", "f5", "f6"}}]

ListLogPlot[timings\[Transpose], Joined -> True]

enter image description here

enter image description here

The numbers on the left side of the table are the length of the list of real numbers for each run.

Note that the graph is logarithmic.

Here are the bytes of memory used while processing the longest list (1,000,000):

enter image description here


After doing the timings above, I found I can make f6 faster by eliminating Apply (@@). It is now clearly the fastest on longer lists. I will not add it to the table above because that is already wide enough, but here is the function and its timings:

f7 = Table[#2[[i]] ~#~ #2[[i + 1]], {i, Length@#2 - 1}] &;

enter image description here

Community
  • 1
  • 1
Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
  • @Margus, I am glad that helps. I am not done with my answer. There are other ways, and I want to add timings, so check back later. – Mr.Wizard May 29 '11 at 14:07
  • @Mr. I am going to add all your functions to my answer in retaliation :) – Dr. belisarius May 29 '11 at 14:45
  • @Mr. I am taking offense of this :). You forgot to include my only contribution in the test. Did I win? :) – Dr. belisarius May 29 '11 at 15:01
  • @Mr. In my score, the one using `Inner` is the fastest, and the deleted one is second. – Dr. belisarius May 29 '11 at 15:07
  • @belisarius, correction made. Did you use the same code, or different? I wonder if `Table` is slower in v8. – Mr.Wizard May 29 '11 at 15:11
  • @Mr. I used my code, very similar to yours. Not much to invent there. The only significant difference I get is that the last function (using Table) is much slower here (by a factor of 6). Perhaps a parallelization issue. – Dr. belisarius May 29 '11 at 15:26
9

As explained in the Mathematica cookbook mapping a function over a moving sublist can be elegantly solved with the ListConvole function:

Map2[f_, l_List] := ListConvolve[{1, 1}, l, {-1, 1}, {}, #2 &, f]

In[11]:= Map2[f, {a, b, c, d}]
Out[11]= {f[a, b], f[b, c], f[c, d]}

Mr.Wizard here, adding comparative timings as requested by sakra.

enter image description here

enter image description here

Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
sakra
  • 62,199
  • 16
  • 168
  • 151
  • I knew I was forgetting something. :-) – Mr.Wizard May 29 '11 at 19:55
  • (grumble mode) I voted for this myself, because it is helpful, but it is **not** the best method here. In fact, it is slower than any of the options I gave. Nothing against you sakra, but I don't think it should rank so highly. – Mr.Wizard May 30 '11 at 08:33
  • @Mr.Wizard Can you add the ListConvole option to your timing test plot? – sakra May 30 '11 at 08:39
  • sakra, may I edit your post to include a comparative graph, or would you prefer that I do not? – Mr.Wizard May 30 '11 at 10:04
  • Done. Observe that `Map2` is uniformly slower than the simple `f1`. `f3` is usually faster than `f1` but it has a strange bobble on the 10^5 list. `f7` starts out slow, but once automatic compilation kicks in, it is easily the fastest. – Mr.Wizard May 30 '11 at 10:47
  • These are really some interesting findings. It shows that ListConvole is just a too general function for the problem at hand. I have upvoted your solution to put you out of grumble mode ;-) – sakra May 30 '11 at 10:57
  • lol, thanks for the "grumble" vote. Indeed, ListConvolve (and ListCorrelate) are very fast with their default functions, but slow down considerably when made general. If it is possible to reformulate a problem in terms of `Times` and `Plus` however, they can be great. – Mr.Wizard May 30 '11 at 11:00
7

I like:

Map2[f_, l_List]:= MapThread[f, {Most@l, Rest@l}]

In[988]:= Map2[f,{a,b,c,d}]
Out[988]= {f[a,b],f[b,c],f[c,d]}
Dr. belisarius
  • 60,527
  • 15
  • 115
  • 190
6

I recently re-read your question and saw your Graph[ ] application.

I think the natural way to do that is:

f[l_] := Graph[Thread[Most@l -> Rest@l]]

So

f[{10, 13, 9, 7, 13, 3, 5, 1, 15, 10, 15, 6, 14, 3, 1, 2, 11, 4, 8, 5}]  

enter image description here

Dr. belisarius
  • 60,527
  • 15
  • 115
  • 190
2

Following belisarius' lead, and addressing your Graph application, this should be a little faster:

f = Inner[Rule, Most@#, Rest@#, Composition[Graph, List]] &;
Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
  • I think that being the current application to display a _directed_ graph, it is doubtful that speed matters _that_ much. A huge Graph is almost illegible :). +1 – Dr. belisarius May 30 '11 at 11:32
  • @belisarius I thought `Graph` was used for other stuff, not just display. No? Thanks for the +1 anyway. I like your solution much better. – Mr.Wizard May 30 '11 at 12:09
  • from the question context seems the display is the objective, but you may well be right. – Dr. belisarius May 30 '11 at 12:13
0

Wow, I saw the other solutions and they seem quite complex and obfuscate. This is a lot simpler to understand, at least if you like a more functional approach:

MapApplyImpl[fun_] := Function[{args}, Apply[fun, args]]
MapApply[fun_, argList_] := Map[MapApplyImpl[fun], argList]

and a usage example:

ff := Function[{t, phi}, t*phi]
MapApply[ff, {{0, Pi/4}, {(Pi/4)/(10^3) , 0}, {(2*Pi/(10^3)), 
   Pi/4}}]
diffeomorphism
  • 991
  • 2
  • 10
  • 27