tags:

views:

543

answers:

6

I have a sequence, e.g

std::vector< Foo > someVariable;

and I want a loop which iterates through everything in it.

I could do this:

for (int i=0;i<someVariable.size();i++) {
    blah(someVariable[i].x,someVariable[i].y);
    woop(someVariable[i].z);
}

or I could do this:

for (std::vector< Foo >::iterator i=someVariable.begin(); i!=someVariable.end(); i++) {
    blah(i->x,i->y);
    woop(i->z);
}

Both these seem to involve quite a bit of repetition / excessive typing. In an ideal language I'd like to be able to do something like this:

for (i in someVariable) {
    blah(i->x,i->y);
    woop(i->z);
}

It seems like iterating through everything in a sequence would be an incredibly common operation. Is there a way to do it in which the code isn't twice as long as it should have to be?

+11  A: 

You could use for_each from the standard library. You could pass a functor or a function to it. The solution I like is BOOST_FOREACH, which is just like foreach in other languages. C+0x is gonna have one btw.

For example:

#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/foreach.hpp>

#define foreach BOOST_FOREACH 

void print(int v)
{
    std::cout << v << std::endl;
}

int main()
{
    std::vector<int> array;

    for(int i = 0; i < 100; ++i)
    {
     array.push_back(i);
    }

    std::for_each(array.begin(), array.end(), print); // using STL

    foreach(int v, array) // using Boost
    {
     std::cout << v << std::endl;
    }
}
AraK
+1: I was about to write the same. In addition: the first for loop isn't as bad in length if a reference to the vector's element is created.
sellibitze
Steve Jessop
+4  A: 

Not counting BOOST_FOREACH which AraK already suggested, you have the following two options in C++ today:

void function(Foo& arg){
  blah(arg.x, arg.y);
  woop(arg.z);
}

std::for_each(someVariable.begin(), someVariable.end(), function); 

struct functor {
  void operator()(Foo& arg){
    blah(arg.x, arg.y);
    woop(arg.z);
  }
};

std::for_each(someVariable.begin(), someVariable.end(), functor());

Both require you to specify the "body" of the loop elsewhere, either as a function or as a functor (a class which overloads operator()). That might be a good thing (if you need to do the same thing in multiple loops, you only have to define the function once), but it can be a bit tedious too. The function version may be a bit less efficient, because the compiler is generally unable to inline the function call. (A function pointer is passed as the third argument, and the compiler has to do some more detailed analysis to determine which function it points to)

The functor version is basically zero overhead. Because an object of type functor is passed to for_each, the compiler knows exactly which function to call: functor::operator(), and so it can be trivially inlined and will be just as efficient as your original loop.

C++0x will introduce lambda expressions which make a third form possible.

std::for_each(someVariable.begin(), someVariable.end(), [](Foo& arg){
  blah(arg.x, arg.y);
  woop(arg.z);
});

Finally, it will also introduce a range-based for loop:

for(Foo& arg : my_someVariable)
{
  blah(arg.x, arg.y);
  woop(arg.z);
}

So if you've got access to a compiler which supports subsets of C++0x, you might be able to use one or both of the last forms. Otherwise, the idiomatic solution (without using Boost) is to use for_eachlike in one of the two first examples.

jalf
Some compilers see the function pointer and inline through it. GCC 4.4 got -findirect-inlining, for example.
Zan Lynx
Yup, like I said, the compiler has to do a much more detailed analysis to figure it out. It's not impossible, and I suspect most compilers will do it in simple cases, but you can't rely on in being inlined in every case. Why make it harder for the compiler than necessary?
jalf
+1  A: 

Prefer algorithm calls to hand-written loops

There are three reasons:

1) Efficiency: Algorithms are often more efficient than the loops programmers produce

2) Correctness: Writing loops is more subject to errors than is calling algorithms.

3) Maintainability: Algorithm calls often yield code that is clearer and more
straightforward than the corresponding explicit loops.

sat
"Algorithm calls often yield code"? Aren't you missing a "better" or "simpler" or something like that?
jalf
yes ,Updated now
sat
A: 
Jerry Coffin
+1  A: 

By the way, MSVS 2008 has a "for each" C++ keyword. Look at How to: Iterate Over STL Collection with for each.

int main() {
   int retval = 0;

   vector<int> col(3);
   col[0] = 10;
   col[1] = 20;
   col[2] = 30;

   for each( const int& c in col )
      retval += c;

   cout << "retval: " << retval << endl;
}
Sergius
A: 
"struct functor {
  void operator()(Foo& arg){
    blah(arg.x, arg.y);
    woop(arg.z);
  }
};

std::for_each(someVariable.begin(), someVariable.end(), functor());"

I think approaches like these are often needlessly baroque for a simple problem.

do i=1,N
 call blah( X(i),Y(i) )
 call woop( Z(i) )
end do

is perfectly clear, even if it's 40 years old (and not C++, obviously).

If the container is always a vector (STL name), I see nothing wrong with an index and nothing wrong with calling that index an integer.

In practice, often one needs to iterate over multiple containers of the same size simultaneously and peel off a datum from each, and do something with the lot of them. In that situation, especially, why not use the index?

As far as SSS's points #2 and #3 above, I'd say it could be so for complex cases, but often iterating 1...N is often as simple and clear as anything else.

If you had to explain the algorithm on the whiteboard, could you do it faster with, or without, using 'i'? I think if your meatspace explanation is clearer with the index, use it in codespace.

Save the heavy C++ firepower for the hard targets.

Matt Kennel