0

I have a for loop that iterates through a vector of objects. If an object doesn't meet a condition, I would like to reiterate the same object through the loop until it meets the condition.

int TrainDog(const vector<Dog> &dogs, const Cat big_cat) { 
    for (auto const dog : dogs) {
       dog->Sit();                 // tell the dog to sit
       if (!dog->IsBarking())      // if dog isn't barking
          dog->Eat(raw_burger);    //   then reward dog
       else {                      // else 
          dog->PlayWith(big_cat);  //   punish dog
          ???                      //   and train again ??? 
          big_cat++;               //   with bigger cat
       } 
    }   
}

I would prefer to keep this clean iterator instead of using the traditional index variable syntax.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
ATL_DEV
  • 9,256
  • 11
  • 60
  • 102
  • 3
    Why not write your loop as `for (auto dog_it = dogs.cbegin(), end = dogs.cend(); dog_it != end;){ ...` and then increment `dog_it` inside the loop body when needed? – alter_igel Jul 10 '18 at 20:59
  • 4
    Or; why not simply use a `while` loop to 'punish and train again' the dog if the dog isn't barking? – alter_igel Jul 10 '18 at 21:00
  • Recursion and goto would immediately present solutions, and I'm assuming no goto. Is recursion ok? – Sean Monroe Jul 10 '18 at 21:02
  • I only care about complexity and performance. – ATL_DEV Jul 10 '18 at 21:04
  • 1
    @SeanMonroe No, these would go into a completely different direction. – πάντα ῥεῖ Jul 10 '18 at 21:04
  • _@ATL_DEV_ Just as @alter said: What's wrong with iterators and incrementing only when needed? – πάντα ῥεῖ Jul 10 '18 at 21:05
  • @πάνταῥεῖ Nothing. How do you do it? – ATL_DEV Jul 10 '18 at 21:14
  • @ATL_DEV Typical case: https://stackoverflow.com/questions/8628951/remove-elements-of-a-vector-inside-the-loop – πάντα ῥεῖ Jul 10 '18 at 21:15
  • 1
    Funny how nobody here picked up that `dogs` is a vector of `Dog` *objects*, not `Dog*` *pointers*, so all of the `dog->` usages need to be `dog.` instead, and `dog` itself should be declared as `Dog&` to avoid making copies. And is `operator++` really implemented for a `const Cat`? This whole code is suspicious. – Remy Lebeau Jul 10 '18 at 21:33
  • @RemyLebeau absolutely, but without the rest of the code we can't be sure. Maybe they have `using Dog = DogImpl*;` or something? The question was about control flow, not the compiler errors those issues would throw up. – JMAA Jul 11 '18 at 08:31

4 Answers4

9

In your case you have the logical concept of repeating an action for each dog, so do exactly that in your code. i.e., write an inner loop that repeats an action for each dog.

For example:

int TrainDog(const vector<Dog> &dogs, const Cat big_cat) { 
    for (auto const dog : dogs) {
       dog->Sit();
       while(dog->IsBarking()) {
          dog->PlayWith(big_cat);
          dog->Sit();
          big_cat++;
       } 
       dog->Eat(raw_burger);
    }   
}
JMAA
  • 1,730
  • 13
  • 24
  • Well yes, why didn't the rest of us see that? But if the dog eats that burger it's going to get sick, better fix that. – Paul Sanders Jul 10 '18 at 21:17
  • 1
    @PaulSanders Well, actually only one dog is eating a burger. Others are eating burger already eaten by another dog, so I guess there are no much problems with that. – V. Kravchenko Jul 10 '18 at 21:19
  • 1
    @VK Oh yes. Except perhaps for the first dog. Hmmm. Now I look more closely, the burger doesn't actually exist at all, it's all rather mysterious. I love the example though. – Paul Sanders Jul 10 '18 at 21:24
  • @PaulSanders I guess that's just a global burger. Perhaps it has already been eaten a lot of times before. – V. Kravchenko Jul 11 '18 at 07:25
3

I can think of couple of options.

  1. Use a normal for loop instead of a range-for loop. Increment the iterator only if certain criteria are met.

    int TrainDog(const vector<Dog> &dogs, const Cat big_cat)
    { 
       for (auto iter = dogs.begin(); iter != dogs.end(); )
       {
          auto dog = *iter;
          dog->Sit();                 // tell the dog to sit
          if (!dog->IsBarking())      // if dog isn't barking
          {          
             dog->Eat(raw_burger);    //   then reward dog
             ++iter;                  // Go on to the next dog
          }
          else
          {
              dog->PlayWith(big_cat);  //   punish dog
              big_cat++;               //   with bigger cat
                                       //   and train again. Don't increment the iterator 
          } 
       }   
    }
    
  2. Use a while loop inside the for loop until some criteria are met.

    int TrainDog(const vector<Dog> &dogs, const Cat big_cat)
    { 
       for (auto const dog : dogs)
       {
          while ( true )
          {
             dog->Sit();                 // tell the dog to sit
             if (!dog->IsBarking())      // if dog isn't barking
             {
                dog->Eat(raw_burger);    //   then reward dog
                break;                   //   break out of the while loop
             }
    
             dog->PlayWith(big_cat);  //   punish dog
             big_cat++;               //   play with bigger cat
          } 
       }   
    }
    
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
R Sahu
  • 204,454
  • 14
  • 159
  • 270
2

As @alter suggested in comments, you can use while loop inside. There is also an option with iterators, but that while-solution is much cleaner. That would just replace condition with while loop, making code even simplier! Another advantage is, you won't have to check iterator every time.

for (auto const dog : dogs) {
   dog->Sit();
   while (dog->IsBarking())
   {
      dog->PlayWith(big_cat);
      big_cat++;
      dog->Sit();
   }
   dog->Eat(raw_burger);
}   
V. Kravchenko
  • 1,859
  • 10
  • 12
1

To reiterate on the same object, create an inner loop.

int TrainDog(const vector<Dog> &dogs, const Cat big_cat) { 
  for (auto const dog : dogs) {
    dog->Sit();                 // tell the dog to sit
    while (dog->IsBarking()) {  // if dog is barking
      dog->PlayWith(big_cat++); //   punish dog
    }                           // and train again 
    dog->Eat(raw_burger);       // then reward dog
   } 
}