Is there an equivalent to the “for … else” Python loop in C++?

前端 未结 14 2032
野的像风
野的像风 2021-01-31 01:21

Python has an interesting for statement which lets you specify an else clause.

In a construct like this one:

for i in foo:
  if         


        
相关标签:
14条回答
  • 2021-01-31 01:46

    There probably isn't a single solution that fits best all problems. In my case a flag variable and a range-based for loop with an auto specifier worked best. Here's an equivalent of the code in question:

    bool none = true;
    for (auto i : foo) {
      if (bar(i)) {
        none = false;
        break;
      }
    }
    if (none) baz();
    

    It is less typing than using iterators. Especially, if you use the for loop to initialize a variable, you may use that instead of the boolean flag.

    Thanks to auto typing it is better than std::none_of, if you want to inline the condition rather than call bar() (and if you are not using C++14).

    I had a situation where both conditions occurred, the code looked something like this:

    for (auto l1 : leaves) {
      for (auto x : vertices) {
        int l2 = -1, y;
        for (auto e : support_edges[x]) {
          if (e.first != l1 && e.second != l1 && e.second != x) {
            std::tie(l2, y) = e;
            break;
          }
        }
        if (l2 == -1) continue;
    
        // Do stuff using vertices l1, l2, x and y
      }
    }
    

    No need for iterators here, because v indicates whether break occurred.

    Using std::none_of would require specifying the type of support_edges[x] elements explicitly in arguments of a lambda expression.

    0 讨论(0)
  • 2021-01-31 01:49

    Direct answer: no, you probably can't, or it is compiler-based, at best. BUT here's a hack of a macro that kind of works!

    A few notes:

    I usually program with Qt, so I'm used to having a foreach loop, and never have to deal with iterators directly.

    I tested this with Qt's compiler (v 5.4.2) but it should work. This is gross for several reasons, but generally does what you'd want. I don't condone coding like this, but there's no reason it shouldn't work as long as you're careful with the syntax.

    #include <iostream>
    #include <vector>
    
    #define for_else(x, y) __broke__ = false; for(x){y} if (__broke__) {}
    #define __break__ __broke__ = true; break
    
    bool __broke__;  // A global... wah wah.
    
    class Bacon {
      public:
        Bacon(bool eggs);
    
        inline bool Eggs() {return eggs_;}
    
      private:
        bool eggs_;
    };
    
    Bacon::Bacon(bool eggs) {
      eggs_ = eggs;
    }
    
    bool bar(Bacon *bacon) {
      return bacon->Eggs();
    }
    
    void baz() {
      std::cout << "called baz\n";
    }
    
    int main()
    {
      std::vector<Bacon *>bacons;
    
      bacons.push_back(new Bacon(false));
      bacons.push_back(new Bacon(false));
      bacons.push_back(new Bacon(false));
    
      for_else (uint i = 0; i < bacons.size(); i++,
          std::cout << bacons.at(i)->Eggs();
          if (bar(bacons.at(i))) {
            __break__;
          }
      ) else {
        baz();
      }
    
      bacons.push_back(new Bacon(true));
      bacons.push_back(new Bacon(false));
    
      for_else (uint i = 0; i < bacons.size(); i++,
          std::cout << bacons.at(i)->Eggs();
          if (bar(bacons.at(i))) {
            __break__;
          }
      ) else {
        baz();
      }
    
      return EXIT_SUCCESS;
    }
    
    0 讨论(0)
  • 2021-01-31 01:51

    It's not only possible in C++, it's possible in C. I'll stick with C++ to make the code comprehensible though:

    for (i=foo.first(); i != NULL || (baz(),0); i = i.next())
    {
        if bar(i):
            break;
    }
    

    I doubt I'd let that through a code review, but it works and it's efficient. To my mind it's also clearer than some of the other suggestions.

    0 讨论(0)
  • 2021-01-31 01:53

    There is no such language construct in C++, but, thanks to the "magic" of the preprocessor, you can make one for yourself. For example, something like this (C++11):

    #include <vector>
    #include <iostream>
    using namespace std;
    
    #define FOR_EACH(e, c, b) auto e = c.begin(); for (; e != c.end(); ++e) {b} if (e == c.end()) {}
    
    int main()
    {
        vector<int> v;
        v.push_back(1);
        v.push_back(2);
    
        FOR_EACH(x, v, {
            if (*x == 2) {
                break;
            }        
            cout << "x = " << *x << " ";
        })
        else {
            cout << "else";
        }
    
        return 0;
    }
    

    This should output x = 1 else.

    If you change if (*x == 2) { to if (*x == 3) {, the output should be x = 1 x = 2.

    If you don't like the fact that a variable is added in the current scope, you can change it slightly:

    #define FOR_EACH(e, c, b, otherwise) {auto e = c.begin(); for (; e != c.end(); ++e) {b} if (e == c.end()) {} otherwise }
    

    then use would be:

    FOR_EACH(x, v, {
        if (*x == 2) {
            break;
        }        
        cout << "x = " << *x << " ";
    },
    else {
        cout << "else";
    })
    

    It's not perfect, of course, but, if used with care, will save you some amount of typing and, if used a lot, would become a part of the project's "vocabulary".

    0 讨论(0)
  • 2021-01-31 01:57

    If doesn't mind using goto also can be done in following way. This one saves from extra if check and higher scope variable declaration.

    for(int i = 0; i < foo; i++)
         if(bar(i))
             goto m_label;
    baz();
    
    m_label:
    ...
    
    0 讨论(0)
  • 2021-01-31 01:58

    A simpler way to express your actual logic is with std::none_of:

    if (std::none_of(std::begin(foo), std::end(foo), bar))
        baz();
    

    If the range proposal for C++17 gets accepted, hopefully this will simplify to:

    if (std::none_of(foo, bar)) baz();
    
    0 讨论(0)
提交回复
热议问题