In Mathematica there are a number of functions that return not only the final result or a single match, but all results. Such functions are named *List. Exhibit:
- FoldList
- NestList
- ReplaceList
- ComposeList
Something that I am missing is a MapList function.
For example, I want:
MapList[f, {1, 2, 3, 4}]
{{f[1], 2, 3, 4}, {1, f[2], 3, 4}, {1, 2, f[3], 4}, {1, 2, 3, f[4]}}
I want a list element for each application of the function:
MapList[
f,
{h[1, 2], {4, Sin[x]}},
{2}
] // Column
{h[f[1], 2], {4, Sin[x]}}
{h[1, f[2]], {4, Sin[x]}}
{h[1, 2], {f[4], Sin[x]}}
{h[1, 2], {4, f[Sin[x]]}}
One may implement this as:
MapList[f_, expr_, level_: 1] :=
MapAt[f, expr, #] & /@
Position[expr, _, level, Heads -> False]
However, it is quite inefficient. Consider this simple case, and compare these timings:
a = Range@1000;
#^2 & /@ a // timeAvg
MapList[#^2 &, a] // timeAvg
ConstantArray[#^2 & /@ a, 1000] // timeAvg
0.00005088
0.01436
0.0003744
This illustrates that on average MapList is about 38X slower than the combined total of mapping the function to every element in the list and creating a 1000×1000 array.
I suspect that
MapListis nearing the performance limit for any transformation that performs structural modification. The existing target benchmarks are not really fair comparisons. TheMapexample is creating a simple vector of integers. TheConstantArrayexample is creating a simple vector of shared references to the same list.MapListshows poorly against these examples because it is creating a vector where each element is a freshly generated, unshared, data structure.I have added two more benchmarks below. In both cases each element of the result is a packed array. The
Arraycase generates new elements by performingListableaddition ona. TheModulecase generates new elements by replacing a single value in a copy ofa. These results are as follows:Note how the new benchmarks perform much more like
MapListand less like theMaporConstantArrayexamples. This seems to show that there is not much scope for dramatically improving the performance ofMapListwithout some deep kernel magic. We can shave a bit of time fromMapListthus:Which yields these timings:
This comes within factor 2 of the
Modulecase and I expect that further micro-optimizations can close the gap yet more. But it is with eager anticipation that I join you awaiting an answer that shows a further tenfold improvement.