development

C ++의 "for… else"Python 루프에 해당하는 것이 있습니까?

big-blog 2020. 12. 1. 18:56
반응형

C ++의 "for… else"Python 루프에 해당하는 것이 있습니까?


파이썬에는 for을 지정할 수 있는 흥미로운 문장이 있습니다 else.

다음과 같은 구조에서 :

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

else절은 뒤에 실행 되지만가 정상적으로 종료되는 for경우에만 실행 for됩니다 (a가 break아님).

C ++에 상응하는 것이 있는지 궁금합니다. 사용할 수 있습니까 for ... else?


실제 논리를 표현하는 더 간단한 방법은 다음과 std::none_of같습니다.

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

C ++ 17에 대한 범위 제안이 수락되면 다음과 같이 단순화되기를 바랍니다.

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

사용해 goto괜찮다면 다음과 같이 할 수 있습니다. 이것은 추가 if검사와 더 높은 범위의 변수 선언 에서 절약됩니다 .

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

이를 위해 람다 함수를 사용할 수 있습니다.

[&](){
  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와 달리) 외부 범위에 액세스 할 수 있습니다.
  • 특수 매크로를 정의하는 것보다 더 읽기 쉽습니다.
  • 특별한 플래그 변수가 필요하지 않습니다.

하지만 ... 나는 투박한 플래그 변수를 사용합니다.


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

다음과 같은 것 :

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

트릭을 수행해야하며 구조화되지 않은 break.


C ++에서만 가능할뿐만 아니라 C에서도 가능합니다. 코드를 이해하기 쉽게 만들기 위해 C ++를 고수하겠습니다.

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

코드 검토를 통해 그렇게 할 수 있을지 의심 스럽지만 효과가 있고 효율적입니다. 내 생각에는 다른 제안보다 더 명확합니다.


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

물론 완벽하지는 않지만주의해서 사용하면 타이핑을 줄일 수 있고 많이 사용하면 프로젝트의 "어휘"의 일부가 될 것입니다.


모든 문제에 가장 적합한 단일 솔루션은 없을 것입니다. 제 경우에는 플래그 변수와 지정자가있는 범위 기반 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
  }
}

No need for iterators here, because v indicates whether break occurred.

Using std::none_of would require specifying the type of support_edges[x] elements explicitly in arguments of a lambda expression.


Direct answer: no, you probably can't, or it is compiler-based, at best. BUT here's a hack of a macro that kind of works!

A few notes:

I usually program with Qt, so I'm used to having a foreach loop, and never have to deal with iterators directly.

I tested this with Qt's compiler (v 5.4.2) but it should work. This is gross for several reasons, but generally does what you'd want. I don't condone coding like this, but there's no reason it shouldn't work as long as you're careful with the syntax.

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

You can use for-else almost like in Python by defining two macros:

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

Now you put the for and the else inside the FORWITHELSE macro separated by a comma and use BREAK instead of break. Here is an example:

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

There are two things you need to remember: to put a comma before the else and to use BREAK instead of break.


I came here because I had the same question, in C though. The best thing I came out with is

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

Explanation: the (notTerminated = false) is an assignment that will always return the false value, it will never affect the condition and will be evaluated iif the condition if true.

참고URL : https://stackoverflow.com/questions/24693694/is-there-an-equivalent-to-the-for-else-python-loop-in-c

반응형