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(child, naughtyList)
iterate(child, naughtyList)
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 selectedMediaList()
does.
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).
Loops are a staple of programming. Simplifying and error-proofing the most common kind of loop is a huge productivity win. foreach regardless of it’s flavor, is worth a try.