是否有一个相当于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),那么感谢auto
input它比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}
现在你把for
和else
放在FORWITHELSE
macros里面,用逗号隔开,用BREAK
而不是break
。 这里是一个例子:
FORWITHELSE( for(int i = 0; i < foo; i++){ if(bar(i)){ BREAK; } }, else{ baz(); } )
有两件事情需要记住:在逗号前加一个逗号,并使用BREAK
而不是break
。