web-dev-qa-db-ja.com

「for ... else」Python C ++のループに相当するものはありますか?

Pythonには、for句を指定できる興味深いelseステートメントがあります。

このような構造では:

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

else節はforの後に実行されますが、forが正常に終了する場合のみ(breakによってではなく)。

C++に同等のものがあるかどうか疑問に思いましたか?使ってもいいですか for ... else

60
Delgan

実際のロジックを表現するより簡単な方法は、 std::none_of

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

C++ 17の範囲提案が受け入れられた場合、これが次のように単純化されることを願っています。

if (std::none_of(foo, bar)) baz();
33
Tony Delroy

gotoの使用を気にしない場合は、次の方法でも実行できます。これにより、余分なifチェックとより高いスコープの変数宣言が不要になります。

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

m_label:
...
21
ifyalciner

はい、次の方法で同じ効果を得ることができます。

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

これは私のC++での大まかな実装です。

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

これにはラムダ関数を使用できます。

[&](){
  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とは異なります)、外部スコープにアクセスできます。
  • 特別なマクロを定義するよりも読みやすくなっています。
  • 特別なフラグ変数は必要ありません。

しかし...私は不格好なフラグ変数を自分で使用します。

10
Noah Black

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

何かのようなもの:

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

トリックを行う必要があり、非構造化breakを回避します。

3
James Kanze

C++だけでなく、Cでも可能です。C++を使用して、コードをわかりやすくします。

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

コードレビューでそれをさせたのではないかと思いますが、うまくいき、効率的です。私の考えでは、他のいくつかの提案よりも明確です。

2
Richard Urwin

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";
})
_

もちろん完璧ではありませんが、注意して使用すると、入力の量をいくらか節約でき、頻繁に使用すると、プロジェクトの「語彙」の一部になります。

1

直接的な答え:いいえ、できません。おそらく、コンパイラベースです。しかし、ここにそのようなマクロのハックがあります!

いくつかのメモ:

私は通常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;
}
0
Charlie

Cでも同じ質問があったので、ここに来ました。一番良かったのは

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

説明:(notTerminated = false)は常に偽の値を返す割り当てであり、条件に影響を与えることはなく、条件が真の場合に評価されます。

0
user1537765

すべての問題に最適な単一のソリューションはおそらくないでしょう。私の場合、フラグ変数と、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
  }
}
_

vbreakが発生したかどうかを示すため、ここにはイテレータは必要ありません。

_std::none_of_を使用するには、ラムダ式の引数で_support_edges[x]_要素の型を明示的に指定する必要があります。

0
arekolek

2つのマクロを定義することにより、Pythonのようにfor-elseを使用できます。

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

ここで、forelseをコンマで区切られたFORWITHELSEマクロ内に配置し、BREAKの代わりにbreakを使用します。以下に例を示します。

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

覚えておく必要があることが2つあります。elseの前にコンマを置くことと、BREAKの代わりにbreakを使用することです。

0
Donald Duck