Why is MapIndexed better than mapping over a range?

Background

I'm working on an application in which I need to create and control two sets of locators. I know from reading the Mathematica documentation and certain posts on Mathematica.SE that this means I can't base my application on a Manipulate expression. I have very little experience with interactive applications based on the dynamic objects that sit below Manipulate, so I searched "multiple locators" to see what I could learn. Quite a bit as it turned out. However, to keep this short, I'll just say I finally settled on a approach described by jVincent in his answer to this question.

Question

In my situation I am going to have one set of locators that are displayed as black squares and a second set displayed as red dots. The application will start out by showing no locators in its content pane. The user will create as many of each kind as he/she wants by clicking on buttons provided for the purpose.

My adaptation of jVincent's code to my situation is as follows:

 DynamicModule[{black = {}, red = {}},
Grid[{{
Framed@Graphics[
{Dynamic@MapIndexed[
With[{i = #2[[1]]},
Locator[Dynamic[black[[i]]],
Style[\[FilledSquare], Black]]]&, black],
Dynamic@MapIndexed[
With[{i = #2[[1]]},
Locator[Dynamic[red[[i]]],
Style[\[FilledCircle], Red]]] &, red]},
PlotRange -> {{0, 1}, {0, 1}}],
SpanFromLeft},


The code works well. I have no complaints. One expression in the code surprises and intrigues me, however. This is:

{Dynamic@MapIndexed[
With[{i = #2[[1]]},
Locator[Dynamic[black[[i]]],
Style[\[FilledSquare], Black]]]&, black]


It's clear what this does: it creates a list of locators based on a list of locations (pairs of real numbers). It's clear that the With trick is needed to work around the HoldFirst attribute of Dynamic. What is clever and surprising -- I would have never thought of it -- is the use of MapIndexed to obtain the indexes that must be inserted into black[[...]]. What I would have thought of is:

 {Dynamic@(With[{i = #},
Locator[Dynamic[black[[i]]], Style[\[FilledSquare], Black]]] &
/@ Range@Length@black)


which is much more banal but gets the job done. I wonder why jVincent chose MapIndexed? Is it really that more efficient than mapping over a Range? Or is there some other deeper advantage beyond my ability to fathom.

I will say I don't think creating a range every time the expression in question is evaluated is much more expensive than what MapIndexed does to create the pairs it uses, but I could be wrong. I'm afraid I'm one those Mathematica programmers who knows the value of everything but the cost of nothing (remembering an old joke made about Lisp programers).

-
I don't think the answer is more complicated than "Why would you write foo[bar[[#]], {#}]& /@ Range@Length@bar when you can simply write MapIndexed[foo, bar]" :) –  rm -rf Nov 15 '12 at 6:05
This occurred to me, but I really wanted to learn what the community and especially jVincent had to say. Thankfully jVincent took the time to write a cogent and detailed answer which should be of general interest. –  m_goldberg Nov 15 '12 at 13:59

I believe I know exactly why jVincent chose MapIndexed rather then mapping over range. As rm-rf states, it does seem semantically simpler, however that's not the reason. It's due to habit and scaling. If for instance you have a 2d array of {x,y} sets that you want to create locators for, I generally write:

 MapIndexed[With[{i = #2}, Locator[Dynamic[array[[Sequence @@ i]]]]] &, array, {-2}]


Which creates the structure based on the information that I want locators using the second lowest level of array, but makes no assumptions on the dimensionality. Therefore it'll work with 1d,2d,nd depending on what array I send it. Because I use this construct rather often, I chose to write something very similar, however I decided against writing out the Sequence to make it simpler for new users to interpret.

All that being said, I almost always chose to use MapIndexed in cases where I want to map over the indexes of some structure, even if I don't need the values simultaneously. Simply because I find it better conveys the intention rather then using map and generating the indexes.

As for efficiency, I would argue that it's misguided to want to optimize a generator for dynamic interactive controls when it's returning in milliseconds, however if you where doing something different and operating on large datasets, it seems that there would be a gain in switching to map:

  SeedRandom[29321];
test = RandomReal[1, 100000];

MapIndexed[With[{i = #2},Locator[Dynamic[test[[Sequence@@i]]]]] &,test];//Timing//First
MapIndexed[With[{i = #2[[1]]}, Locator[Dynamic[test[[i]]]]] &, test];// Timing//First
Map[With[{i = #}, Locator[Dynamic[test[[i]]]]] &, Range@Length@test];// Timing//First


0.297

0.375

0.281

However in this case, if you where creating 100000 locators for a single graphic, you would most likely have other problems than the time it took to create them.

-