Is there an equivalent to the “for … else” Python

2019-03-09 03:38发布

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

In a construct like this one:

for i in foo:
  if bar(i):
    break
else:
  baz()

the else clause is executed after the for, but only if the for terminates normally (not by a break).

I wondered if there was an equivalent in C++? Can I use for ... else?

12条回答
相关推荐>>
2楼-- · 2019-03-09 04:15

You can use for-else almost like in Python by defining two macros:

#define BREAK {CONTINUETOELSE = false; break;}
#define FORWITHELSE(x, y) {bool CONTINUETOELSE = true; x if(!CONTINUETOELSE){} y}

Now you put the for and the else inside the FORWITHELSE macro separated by a comma and use BREAK instead of break. Here is an example:

FORWITHELSE(
    for(int i = 0; i < foo; i++){
        if(bar(i)){
            BREAK;
        }
    },
    else{
        baz();
    }
)

There are two things you need to remember: to put a comma before the else and to use BREAK instead of break.

查看更多
你好瞎i
3楼-- · 2019-03-09 04:19

This is my rough implementation in C++:

bool other = true;
for (int i = 0; i > foo; i++) {
     if (bar[i] == 7) {
          other = false;
          break;
     }
} if(other)
     baz();
查看更多
该账号已被封号
4楼-- · 2019-03-09 04:21

I am not aware of an elegant way to accomplish this in C/C++ (not involving a flag variable). The suggested other options are much more horrible than that...

To answer @Kerrek SB about real life usages, I found a few in my code (simplified snippets)

Example 1: typical find/fail

for item in elements:
    if condition(item):
        do_stuff(item)
        break
else: #for else
    raise Exception("No valid item in elements")

Example 2: limited number of attempts

for retrynum in range(max_retries):
    try:
        attempt_operation()
    except SomeException:
        continue
    else:
        break
else: #for else
    raise Exception("Operation failed {} times".format(max_retries))
查看更多
我欲成王,谁敢阻挡
5楼-- · 2019-03-09 04:26

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.

查看更多
Explosion°爆炸
6楼-- · 2019-03-09 04:27

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;
}
查看更多
▲ chillily
7楼-- · 2019-03-09 04:28

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".

查看更多
登录 后发表回答