是否有一个相当于C ++的“for … else”Python循环?

Python有一个有趣for陈述,可以让你指定一个else子句。

在这样一个构造中:

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

else子句在for之后执行,但是只有在for正常终止(而不是break )的情况下。

我想知道是否有一个相当于C + +? 我可以使用for ... else

expression实际逻辑的一个简单方法是使用std::none_of

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

如果C ++ 17的范围build议被接受,希望这将简化为:

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

如果不介意使用goto也可以按照以下方式完成。 if检查和更高范围的variables声明,这个从额外存储。

 for(int i = 0; i < foo; i++) if(bar(i)) goto m_label; baz(); m_label: ... 

是的,您可以通过以下方式达到相同

 auto it = std::begin(foo); for (; it != std::end(foo); ++it) if(bar(*it)) break; if(it == std::end(foo)) baz(); 

这是我在C ++中的粗略实现:

 bool other = true; for (int i = 0; i > foo; i++) { if (bar[i] == 7) { other = false; break; } } if(other) baz(); 

你可以使用这个lambda函数:

 [&](){ for (auto i : foo) { if (bar(i)) { // early return, to skip the "else:" section. return; } } // foo is exhausted, with no item satisfying bar(). ie, "else:" baz(); }(); 

这应该像Python的“for..else”一样行事,它比其他解决scheme有一些优势:

  • 这是“for..else”的一个真正的插入式replace:“for”部分可以有副作用(与none_of不同,它的谓词不能修改它的参数),它可以访问外部作用域。
  • 这比定义一个特殊的macros更可读。
  • 它不需要任何特殊的标志variables。

但是…我会用笨重的标志variables,我自己。

就像是:

 auto it = foo.begin(), end = foo.end(); while ( it != end && ! bar( *it ) ) { ++ it; } if ( it != foo.end() ) { baz(); } 

应该做的伎俩,它避免了非结构性的break

我不知道在C / C ++(不涉及标志variables)完成这个优雅的方式。 build议的其他选项比这更可怕

为了回答@Kerrek SB关于真实生活的用法,我在代码中find了一些(简化代码片段)

示例1:典型的查找/失败

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

例2:有限次数的尝试

 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)) 

它不仅可以在C ++中使用,而且可以在C中使用。我将坚持使用C ++来使代码易于理解:

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

我怀疑我会通过代码审查,但它的作品,它是有效的。 在我看来,它比其他一些build议更清晰。

在C ++中没有这样的语言结构,但是,由于预处理器的“魔力”,您可以为自己创build一个。 例如,像这样的东西(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; } 

这应该输出x = 1 else

如果你改变if (*x == 2) { if (*x == 3) { ,输出应该是x = 1 x = 2

如果您不喜欢在当前范围中添加variables的事实,则可以稍微更改它:

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

那么使用将是:

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

当然,这并不完美,但如果小心使用,会节省一些打字量,如果使用了很多,就会成为项目“词汇”的一部分。

可能没有一个适合所有问题的解决scheme。 在我的情况下,一个标志variables和一个基于范围的for循环与auto说明符效果最好。 下面是相关的代码:

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

它比使用迭代器的inputless。 特别是,如果你使用for循环来初始化一个variables,你可以使用它来代替布尔标志。

如果你想内联条件而不是调用bar() (如果你没有使用C ++ 14),那么感谢autoinput它比std::none_of要好。

我遇到了两种情况都出现的情况,代码看起来像这样:

 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 } } 

这里不需要迭代器,因为v表示是否发生break

使用std::none_of将需要在lambdaexpression式的参数中明确指定support_edges[x]元素的types。

直接回答:不,你可能不能,或者它最好是基于编译器的。 但是,这是一个macros的黑客这种工作!

一些注意事项:

我通常使用Qt编程,所以我习惯了foreach循环,而不必直接处理迭代器。

我用Qt的编译器(v 5.4.2)testing过,但是它应该可以工作。 这是总结有几个原因,但一般做你想要的。 我不会宽恕这样的编码,但没有理由只要你小心语法就不应该工作。

 #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; } 

通过定义两个macros,你可以像使用Python一样使用for-else:

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

现在你把forelse放在FORWITHELSEmacros里面,用逗号隔开,用BREAK而不是break 。 这里是一个例子:

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

有两件事情需要记住:在逗号前加一个逗号,并使用BREAK而不是break