有没有等效于C ++中的“ for ... else” Python循环?

Python有一个有趣的for语句,可让您指定for ... else子句。

在这样的结构中:

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

for ... else子句在for之后执行,但前提是for正常终止(而不是break)。

我想知道C ++中是否有等效的东西? 我可以使用for ... else吗?

Delgan asked 2019-10-09T14:55:01Z
13个解决方案
33 votes

表示实际逻辑的更简单方法是使用std::none_of

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

如果针对C ++ 17的范围建议被接受,则希望它将简化为:

if (std::none_of(foo, bar)) baz();
Tony Delroy answered 2019-10-09T14:55:29Z
21 votes

如果不介意使用goto,也可以按照以下方式进行。 这节省了额外的if检查和更高范围的变量声明。

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

m_label:
...
ifyalciner answered 2019-10-09T14:55:58Z
13 votes

是的,您可以通过以下方法达到相同的效果:

auto it = std::begin(foo);
for (; it != std::end(foo); ++it)
     if(bar(*it))
         break;
if(it == std::end(foo))
    baz();
haccks answered 2019-10-09T14:56:30Z
11 votes

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

bool other = true;
for (int i = 0; i > foo; i++) {
     if (bar[i] == 7) {
          other = false;
          break;
     }
} if(other)
     baz();
Easton answered 2019-10-09T14:56:58Z
10 votes

您可以为此使用lambda函数:

[&](){
  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..else”的真正替代品:“ for”部分可能会有副作用(与none_of不同,其谓词不得修改其参数),并且可以访问外部作用域。
  • 比定义特殊宏更具可读性。
  • 它不需要任何特殊的标志变量。

但是...我自己会使用笨拙的flag变量。

Noah Black answered 2019-10-09T14:58:07Z
4 votes

我不知道在C / C ++中实现这一目标的优雅方法(不涉及标志变量)。 建议的其他选项比这更可怕...

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

示例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))
agomcas answered 2019-10-09T14:59:02Z
3 votes

就像是:

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

应该可以解决问题,并且避免了非结构化break

James Kanze answered 2019-10-09T14:59:44Z
2 votes

这不仅在C ++中是可能的,而且在C中也是可能的。我将坚持使用C ++使代码易于理解:

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

我怀疑我会通过代码审查来做到这一点,但是它有效且有效。 在我看来,它比其他一些建议还清晰。

Richard Urwin answered 2019-10-09T15:00:26Z
1 votes

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

这应该输出if (*x == 2) {

如果将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";
})

当然,它并不完美,但是,如果谨慎使用,它将节省您大量的打字时间,如果使用过多,它将成为项目“词汇表”的一部分。

srdjan.veljkovic answered 2019-10-09T15:01:40Z
0 votes

可能没有一个适合所有问题的解决方案。 在我的情况下,标记变量和带有support_edges[x]说明符的基于范围的std::none_of循环效果最佳。 这等效于所讨论的代码:

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

它的键入少于使用迭代器。 特别是,如果使用std::none_of循环初始化变量,则可以使用该变量代替布尔标志。

如果要内联条件而不是调用support_edges[x](并且如果您不使用C ++ 14),则由于键入了std::none_of,它比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
  }
}

这里不需要迭代器,因为std::none_of指示是否发生support_edges[x]

使用std::none_of将需要在lambda表达式的参数中显式指定support_edges[x]元素的类型。

arekolek answered 2019-10-09T15:02:57Z
0 votes

直接答案:不,您可能不能,或者充其量是基于编译器的。 但是这里是一个宏的工作原理!

一些注意事项:

我通常使用Qt进行编程,因此我习惯于使用foreach循环,而不必直接处理迭代器。

我用Qt的编译器(v 5.4.2)测试了它,但是它应该可以工作。 出于某些原因,这很麻烦,但通常会满足您的要求。 我不宽容这样的编码,但是只要您对语法保持谨慎,就没有理由不起作用。

#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;
}
Charlie answered 2019-10-09T15:03:54Z
0 votes

您可以像定义Python一样使用for-else,方法是定义两个宏:

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

现在,您将elseBREAK放在break宏内并以逗号分隔,并使用BREAK代替break。这是一个示例:

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

您需要记住两件事:在else前面加上逗号,并使用BREAK而不是break

Donald Duck answered 2019-10-09T15:04:42Z
0 votes

我来到这里是因为我在C语言中也有同样的问题。我想到的最好的事情是

bool notTerminated = true;
for (int i = 0; i < 50 || (notTerminated = false); i++)
    if (bar(i))
        break;
if (! notTerminated)
    baz();

说明:(notTerminated = false)是始终会返回假值的赋值,它将永远不会影响条件,并且如果条件为true,则将对其进行评估。

user1537765 answered 2019-10-09T15:05:24Z
translate from https://stackoverflow.com:/questions/24693694/is-there-an-equivalent-to-the-for-else-python-loop-in-c