Есть ли эквивалент цикла "for... else" Python в С++?
У Python есть интересный оператор for
, который позволяет указать предложение else
.
В конструкции, подобной этой:
for i in foo:
if bar(i):
break
else:
baz()
предложение else
выполняется после for
, но только если for
завершается нормально (не с помощью <<26 > ).
Я задавался вопросом, был ли эквивалент в С++? Могу ли я использовать for ... else
?
Ответы
Ответ 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
) только в одном месте.