Подтвердить что ты не робот

Есть ли эквивалент цикла "for... else" Python в С++?

У Python есть интересный оператор for, который позволяет указать предложение else.

В конструкции, подобной этой:

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

предложение else выполняется после for, но только если for завершается нормально (не с помощью <<26 > ).

Я задавался вопросом, был ли эквивалент в С++? Могу ли я использовать for ... else?

4b9b3361

Ответ 1

Более простой способ выразить вашу действительную логику с помощью std::none_of:

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

Если предложение по диапазону для С++ 17 будет принято, надеюсь, это упростит до:

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

Ответ 2

Если вы не можете использовать goto, это также можно сделать следующим образом. Это экономит дополнительную информацию об объявлении переменной if и выше.

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

m_label:
...

Ответ 3

Да, вы можете добиться того же эффекта:

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

Ответ 4

Это моя грубая реализация в С++:

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

Ответ 5

Для этого можно использовать функцию лямбда:

[&](){
  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, предикат которого не должен изменять свой аргумент), и он имеет доступ к внешней области.
  • Это более читаемо, чем определение специального макроса.
  • Он не требует каких-либо специальных флаговых переменных.

Но... Я бы использовал неуклюжую флаговую переменную.

Ответ 6

Я не знаю об элегантном способе выполнить это в 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))

Ответ 7

Что-то вроде:

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

должен сделать трюк, и он избегает неструктурированного break.

Ответ 8

Это не только возможно в С++, это возможно в C. Я буду придерживаться С++, чтобы сделать код понятным, хотя:

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

Я сомневаюсь, что позволил бы это через обзор кода, но он работает и эффективен. На мой взгляд, это также яснее, чем некоторые другие предложения.

Ответ 9

В С++ нет такой языковой конструкции, но, благодаря "магии" препроцессора, вы можете сделать ее самостоятельно. Например, что-то вроде этого (С++ 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";
})

Это не идеально, конечно, но, если использовать с осторожностью, сэкономит вам некоторую информацию и, если ее использовать много, станет частью проекта "лексика".

Ответ 10

Вероятно, не существует ни одного решения, которое бы отвечало самым лучшим требованиям. В моем случае лучшая была переменная флага и цикл for на основе диапазона с конструктором auto. Здесь эквивалент рассматриваемого кода:

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

Это меньше, чем используя итераторы. Особенно, если вы используете цикл for для инициализации переменной, вы можете использовать это вместо логического флага.

Благодаря auto типу лучше, чем std::none_of, если вы хотите включить условие, а не вызвать bar() (и если вы не используя С++ 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 потребовало бы явно указать тип элементов support_edges[x] в аргументах лямбда-выражения.

Ответ 11

Прямой ответ: нет, вы, вероятно, не можете, или в лучшем случае на компиляторе. НО здесь взломать макрос такого рода работ!

Несколько примечаний:

Я обычно программирую с 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;
}

Ответ 12

Вы можете использовать for-else почти как в Python, задав два макроса:

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

Теперь вы помещаете for и else в макрос FORWITHELSE, разделенный запятой, и используйте BREAK вместо BREAK. Вот пример:

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

Вам нужно запомнить две вещи: поставить запятую перед else и использовать BREAK вместо BREAK.

Ответ 13

Я пришел сюда, потому что у меня был тот же вопрос, хотя и на Си. Лучшее, что я получил, это

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

Объяснение: (notTerminated = false) является присваиванием, которое всегда будет возвращать ложное значение, оно никогда не повлияет на условие и будет оцениваться, если условие имеет значение true.

Ответ 14

Я бы сделал это с помощью простой вспомогательной переменной:

#include <stdio.h>
#include <stdbool.h>

int main()

{
    bool b;
    printf("Numbers which are multiples of 7:\n");

    for (int i=8; b=(i<12); i++)
    {
        if (i%7==0)
        {
            printf("%d", i);
            break;
        }
    }
    if (!b)
    {
        printf("no numbers found\n");
    }
    return 0;
}

Таким образом, вам нужно реализовать условие (в приведенном выше примере i<12) только в одном месте.