I love foreach. What I really mean is that I really like dead-simple ways to iterate over the items in a container, one at a time. It goes beyond syntactic sugar; making errors harder to write, and easier to spot.
I’m using the term “foreach”, because the first time I realized the utility of simple iteration was seeing Michael Tsai’s foreach macro. I instantly fell in love with the ‘keyword’, because it greatly simplifies iterating over items in a Cocoa container. My reaction was so strong, because the (then current) Cocoa iteration idiom I was using was so bad. As Jonathan ‘Wolf’ Rentzsch says:
I have a number of issues with enumeration in Cocoa/ObjC. Indeed, I have a problem with every one of the three lines necessary in the standard idiom. It even goes beyond that — I have an problem with the very fact it’s three lines of code versus one.
Objective-C 2.0 (Leopard and later) introduced Fast Enumeration. It’s a better way of handling enumeration then Michael Tsai’s macro, but if you are stuck using Objective-C 1.0, then I highly recommend using his foreach macro.
I do not like the C++ stl for_each idiom. It’s not simple enough.
To explain what’s wrong with
for_each I should explain what a “foreach” should look like. It should be a two-argument construct: “
foreach (item, container)“, and no more.
container should be evaluated only once. Syntax details aren’t terribly important, as long as they make sense.
ForEach(String child, naughtyList)
for child in naughyList:
for(NSString *child in naughtyList)
Are all fine constructs.
The most obvious benefits of a foreach is that it’s less code, easier to write, and less work to read. Implemented correctly, it also makes bugs harder to write — mostly because it minimizes side effects in the loop construct. For example, do you see the bug in the following code?
for( list<shared_ptr<const Media> >::iterator it = selectedMedias->selectedMediaList().begin(); it != selectedMedias->selectedMediaList().end(); ++it )
I didn’t; and it’s kind of a trick question.
->selectedMediaList() isn’t an accessor function — it constructs a new list every time it is called. So
selectedMediaList() != selectedMediaList() because it returns a different list each time. The loop never terminates because
it will never equal
end(), since it is the end of a different list. But you have no way of knowing this without knowing details of what
Using BOOST_FOREACH avoids the problem:
BOOST_FOREACH(shared_ptr<const CSMediaLib::Media> media, selectedMedias->selectedMediaList())
works regardless of how
selectedMediaList() is implemented, because it is only evaluated once. It’s also easier to write, and to read. I haven’t used BOOST_FOREACH much, but it’s been totally positive so far. (Yes, the name is ugly, but that’s not important).