4

I was wondering if anyone had any thoughts on how to iterate through the elements of a vector (or container) when the direction of the iteration is input.

This was the first thing I could come up with:

std::vector<int> vec = {1, 5, 7, 23};
int direction = 1 // or -1;
int start = direction == 1 ?  0 : (int)arrs.size()-1;
for (int i=start; i<(int)vec.size() && 0<=i; i+=direction) {
    //do_stuff_fn(i, vec.at(i))
}

Does anyone know any better or nicer way to do this? And please I need to have access to the index i in the loop. I'm afraid that this means that the std::for_each is not an option.

Deidrei
  • 2,125
  • 1
  • 14
  • 14
ormurin
  • 358
  • 3
  • 11
  • 3
    Having two different loops for forward and reverse would be more readable in my opinion. Moreover, you will not need to convert the size() to type int. – Abhishek Bansal Dec 13 '13 at 08:28

4 Answers4

1

There is a very simple solution indeed: use two loops.

void doit(size_t index, std::vector<int>& v) { ... what ever ... }

void function(std::vector<int>& v, bool const ascending) {
    if (ascending) {
        for (size_t i = 0, max = v.size(); i != size; ++i) { doit(i, v); }
    } else {
        for (size_t i = v.size(); i > 0; --i) { doit(i-1, v); }
    }                                         //      ^^ crucial bit here!
}

With C++11, doit would be a lambda that captures v by reference:

void function(std::vector<int>& v, bool const ascending) {
    auto doit = [&v](size_t index) { .... };

    if (ascending) {
        for (size_t i = 0, max = v.size(); i != size; ++i) { doit(i); }
    } else {
        for (size_t i = v.size(); i > 0; --i) { doit(i-1); }
    }                                         //      ^^ crucial bit here!
}
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • This is pretty much the same as my code? Only you have a separate function call in every iteration – ormurin Dec 13 '13 at 10:59
  • @ormurin: yes, the trick is that there is no trick. Trying to fold both forms of iterations conceals the intent in cleverness, whilst laying it out in clear form is both functional and readable. The catch ? You have to write a function/lambda for the inner body of the loop to avoid repetition. I personally find it preferable (especially with lambdas), but it is indeed subjective as all matters of style. – Matthieu M. Dec 13 '13 at 13:07
  • What is the purpose of `max = v.size()` here? Actually, I have never seen a for loop like this. – Matsmath Oct 31 '19 at 23:33
  • 1
    @Matsmath: It's an optimization, to help the optimizer. With a typical loop (`for (size_t i = 0; i != v.size(); ++i)`) the optimizer may not realize that `v.size()` is never modified in the loop. Querying `v.size()` does not cost much, in itself, however it prevents optimizations such as loop unrolling and vectorization. By explicitly indicating to the compiler that the loop bounds are fixed ahead of time, you facilitate its work. This also applies to `v.end()`, which range-for loops (C++11) automatically "cache" for you. – Matthieu M. Nov 01 '19 at 07:40
0

Since you want to use c++ and std library, there is nothing more suitable than reverse_iterator.

example:

std::vector<int> vec = {1, 5, 7, 23};
int direction = 1 // or -1;
auto begin =  (direction > 0) ? vec.begin() : ver.rbegin();
auto end =  (direction > 0) ? vec.end() : ver.rend();
for (auto it = begin; it != end; it++)
{
    do_stuff_fn(it - vec.begin(), *it);
}
V-X
  • 2,979
  • 18
  • 28
0
void printer(std::vector<int>& v, int d) {
    for (auto i = d > 0 ? 0 : v.size() - 1;
            i != (d > 0 ? v.size() : -1);
            i += d) {
        std::cout << v[i] << std::endl;
    }   
}
perreal
  • 94,503
  • 21
  • 155
  • 181
  • Isn't this an infinite loop if `d <= 0`? (Due to unsigned promotion) Hmm. Oh, I see, this function is only valid for d=1 and d=-1. – dalle Dec 13 '13 at 09:11
  • The pattern is pretty as I suggested? Only not very readable? – ormurin Dec 13 '13 at 11:00
0

You may try this:

class idx{
private:
   bool forwardDir;
   int current;
   bool complete;
   int s;
public:
   idx(int size,bool forward)
   {
       s = size;
       complete = false;
       forwardDir = forward;
       if(forward)
          current = 0;
       else
          current = size-1;
   }
   int operator ++(int)
   {
       if(forwardDir)
       {
           ++current;
           if(current==s)
               complete = true;
           return current;
       }
       else
       {
           --current;
           if(current ==-1)
              complete=true
           return current;
       }
   }
   bool notComplete()
   {
       return !complete;
   }
   explicit operator int() const { return current; }
}

Now you may use this idx class in vector iteration. e.g.

std::vector<int> vec = {1, 5, 7, 23};
int direction = 1 // or -1; 
bool dir;
if(direction == 1)
    dir = 1;
else
    dir = 0
idx i(vec.size(),dir);
for (;i.notComplete();i++)
{
    // do staff with vec.at(i);
}
deeiip
  • 3,319
  • 2
  • 22
  • 33