问题
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
?
回答1:
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();
回答2:
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:
...
回答3:
Yes you can achieve the same effect by:
auto it = std::begin(foo);
for (; it != std::end(foo); ++it)
if(bar(*it))
break;
if(it == std::end(foo))
baz();
回答4:
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();
回答5:
You could use a lambda function for this:
[&](){
for (auto i : foo) {
if (bar(i)) {
// early return, to skip the "else:" section.
return;
}
}
// foo is exhausted, with no item satisfying bar(). i.e., "else:"
baz();
}();
This should behave exactly like Python's "for..else", and it has some advantages over the other solutions:
- It's a true drop-in replacement for "for..else": the "for" section can have side effects (unlike none_of, whose predicate must not modify its argument), and it has access to the outer scope.
- It's more readable than defining a special macro.
- It doesn't require any special flag variables.
But... I'd use the clunky flag variable, myself.
回答6:
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))
回答7:
Something like:
auto it = foo.begin(), end = foo.end();
while ( it != end && ! bar( *it ) ) {
++ it;
}
if ( it != foo.end() ) {
baz();
}
should do the trick, and it avoids the unstructured break
.
回答8:
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.
回答9:
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".
回答10:
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.
回答11:
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;
}
回答12:
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
.
回答13:
I came here because I had the same question, in C though. The best thing I came out with is
bool notTerminated = true;
for (int i = 0; i < 50 || (notTerminated = false); i++)
if (bar(i))
break;
if (! notTerminated)
baz();
Explanation: the (notTerminated = false)
is an assignment that will always return the false value, it will never affect the condition and will be evaluated iif the condition if true.
回答14:
I would accomplish this with a simple helper variable:
#include <stdio.h>
#include <stdbool.h>
int main()
{
bool b;
printf("Numbers which are multiples of 7:\n");
for (int i=8; b=(i<12); i++)
{
if (i%7==0)
{
printf("%d", i);
break;
}
}
if (!b)
{
printf("no numbers found\n");
}
return 0;
}
This way, you need to implement the condition (in the above examplei<12
) only at one place.
来源:https://stackoverflow.com/questions/24693694/is-there-an-equivalent-to-the-for-else-python-loop-in-c