Python具有一个有趣的for
语句,它允许您指定一个else
子句。
在类似于这样的结构中:
for i in foo:
if bar(i):
break
else:
baz()
else
子句会在for
循环执行完毕后被执行,但仅当for
正常结束时(非通过break
)。
我想知道C++中是否有等价的功能?我能使用for ... else
吗?
如果不介意使用 goto
,也可以按以下方式完成。这种方法可以避免额外的 if
检查和更高范围变量声明。
for(int i = 0; i < foo; i++)
if(bar(i))
goto m_label;
baz();
m_label:
...
一个更简单表达实际逻辑的方法是使用std::none_of
:
if (std::none_of(std::begin(foo), std::end(foo), bar))
baz();
if (std::none_of(foo, bar)) baz();
这是我的C++草稿实现:
bool other = true;
for (int i = 0; i > foo; i++) {
if (bar[i] == 7) {
other = false;
break;
}
} if(other)
baz();
i > foo
?不应该是 i < foo
吗? - ferdynator是的,您可以通过以下方式实现相同的效果:
auto it = std::begin(foo);
for (; it != std::end(foo); ++it)
if(bar(*it))
break;
if(it == std::end(foo))
baz();
end()
相比可能很容易调整。 - user395760i
可能从来不是整数。这更像是C++11的 for (auto i : foo)
循环。 - user395760for(auto it = begin(something); it != end(something); ++i)
这行代码意为:对于定义的迭代器it,它从something的起始位置开始,直到遍历完整个something(不包括结尾位置),每一次循环增加一个元素进行迭代。 if (it == end(something))
判断当前迭代器it是否已经到达了something的末尾。另外请记住,每当您进行不必要的后缀递增时,都会有一只小猫死去。 - m.wasowskii++
可能会慢得多,因为它涉及创建一个新对象;在像 ++i
可以使用的地方,使用它是一个坏习惯,在体面的代码中你不会找到它。就个人而言,它迫使我不必要地减慢速度并思考是否有理由使用它,或者只是草率的代码。即使只有一秒钟,这可能也很烦人。 - m.wasowskiint
那样是原始类型。此外,请查看下面的评论,您将找到所有使用++i
作为默认值的原因。而且,在我看来,出于一致性考虑,原始类型(如int
)也应该使用相同的方式。 - m.wasowski[&](){
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();
}();
这应该像Python的"for..else"一样工作,并且相比其他解决方案具有一些优点:
但是...我会自己使用笨拙的标志变量。
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))
any
和map
或迭代器推导式替换为if语句。 - Андрей Беньковский类似于:
auto it = foo.begin(), end = foo.end();
while ( it != end && ! bar( *it ) ) {
++ it;
}
if ( it != foo.end() ) {
baz();
}
应该就万事大吉了,而且它避免了无组织的break
。
break
是可以的,而且无论如何,这种改变与实际内容(it != foo.end()
)完全不相关。 - user395760while
循环;我会修复它的。 - James Kanze在C++中没有这样的语言结构,但是由于预处理器的“魔力”,您可以自己创建一个。例如,像这样的东西(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
。#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";
})
当然不是完美的,但是如果小心使用,可以节省一些打字时间,而且如果经常使用,它会成为项目“词汇”的一部分。
x
。没有什么好的方法可以避免这个问题... - nonsensickle这不仅在C++中可以实现,而且在C语言中也可以实现。为了使代码易于理解,我将继续使用C++:
for (i=foo.first(); i != NULL || (baz(),0); i = i.next())
{
if bar(i):
break;
}
我怀疑我不会在代码审查中通过这个,但它能够工作且高效。在我看来,这也比其他一些建议更清晰。
可能没有一种解决方案适用于所有问题。在我的情况下,一个标志变量和一个基于范围的for
循环与auto
说明符最好地工作。这是相关代码的等效形式:
bool none = true;
for (auto i : foo) {
if (bar(i)) {
none = false;
break;
}
}
if (none) baz();
这比使用迭代器打字更少。特别是,如果您使用for
循环来初始化变量,您可以使用它来代替布尔标志。
由于auto
类型推断,它比std::none_of
更好,如果您想要内联条件而不是调用bar()
(并且如果您没有使用C++14)。
我遇到了两种情况,代码看起来像这样:
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
需要在lambda表达式的参数中显式指定support_edges[x]
元素的类型。
if (c)
、while (c)
和for (...; c; ...)
这三者都会评估条件c
,但目前只有第一个能让你得到表达式的结果。其他两个则丢弃了这个信息,这违反了不应该丢弃作为计算一部分获得的结果的原则。 - Kerrek SBnot break
语句块 的好处在于使用现有的关键字,以实现更好的向后兼容性(并且强烈表明其运行条件)。在C++中,使用else
会与现有代码冲突,例如if (...) for (...) ...; else ...
。 - Tony Delroy