Python a une instruction for
intéressante qui vous permet de spécifier une clause else
.
Dans une construction comme celle-ci:
for i in foo:
if bar(i):
break
else:
baz()
la clause else
est exécutée après la for
, mais uniquement si la for
se termine normalement (et non par break
).
Je me suis demandé s'il y avait un équivalent en C++? Puis-je utiliser for ... else
?
Un moyen plus simple d'exprimer votre logique réelle est d'utiliser std::none_of
:
if (std::none_of(std::begin(foo), std::end(foo), bar))
baz();
Si la proposition de plage pour C++ 17 est acceptée, nous espérons que cela simplifiera les choses:
if (std::none_of(foo, bar)) baz();
Si cela ne vous dérange pas d'utiliser goto
, vous pouvez également le faire de la manière suivante. Celui-ci enregistre les chèques extra if
et la déclaration de variable de portée supérieure.
for(int i = 0; i < foo; i++)
if(bar(i))
goto m_label;
baz();
m_label:
...
Oui, vous pouvez obtenir le même effet en:
auto it = std::begin(foo);
for (; it != std::end(foo); ++it)
if(bar(*it))
break;
if(it == std::end(foo))
baz();
Voici mon implémentation brute en C++:
bool other = true;
for (int i = 0; i > foo; i++) {
if (bar[i] == 7) {
other = false;
break;
}
} if(other)
baz();
Vous pouvez utiliser une fonction lambda pour cela:
[&](){
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();
}();
Cela devrait se comporter exactement comme "for..else" de Python et présenter certains avantages par rapport aux autres solutions:
Mais ... j'utiliserais la variable drapeau maladroite, moi-même.
Je ne suis pas au courant d'une manière élégante d'accomplir cela en C/C++ (n'impliquant pas une variable de drapeau). Les autres options suggérées sont beaucoup plus horribles que cela ...
Pour répondre à @Kerrek SB à propos d’usages réels, j’en ai trouvé quelques-uns dans mon code (extraits simplifiés).
Exemple 1: recherche/échec typique
for item in elements:
if condition(item):
do_stuff(item)
break
else: #for else
raise Exception("No valid item in elements")
Exemple 2: nombre de tentatives limité
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))
Quelque chose comme:
auto it = foo.begin(), end = foo.end();
while ( it != end && ! bar( *it ) ) {
++ it;
}
if ( it != foo.end() ) {
baz();
}
devrait faire l'affaire, et il évite le break
non structuré.
Ce n'est pas seulement possible en C++, c'est aussi possible en C. Je vais m'en tenir au C++ pour rendre le code compréhensible:
for (i=foo.first(); i != NULL || (baz(),0); i = i.next())
{
if bar(i):
break;
}
Je doute que je laisse cela à travers une révision du code, mais cela fonctionne et est efficace. À mon avis, cela est également plus clair que certaines des autres suggestions.
Ce langage n'existe pas en C++, mais grâce à la "magie" du préprocesseur, vous pouvez en créer un vous-même. Par exemple, quelque chose comme ceci (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;
}
Cela devrait générer x = 1 else
.
Si vous remplacez if (*x == 2) {
par if (*x == 3) {
, le résultat devrait être x = 1 x = 2
.
Si vous n'aimez pas le fait qu'une variable soit ajoutée à la portée actuelle, vous pouvez la modifier légèrement:
#define FOR_EACH(e, c, b, otherwise) {auto e = c.begin(); for (; e != c.end(); ++e) {b} if (e == c.end()) {} otherwise }
alors utiliser serait:
FOR_EACH(x, v, {
if (*x == 2) {
break;
}
cout << "x = " << *x << " ";
},
else {
cout << "else";
})
Ce n'est pas parfait, bien sûr, mais si vous l'utilisez avec précaution, vous économiserez un peu de dactylographie et, s'il est beaucoup utilisé, il fera partie du "vocabulaire" du projet.
Réponse directe: non, vous ne pouvez probablement pas, ou au mieux, il est basé sur un compilateur. MAIS voici un morceau de macro qui fonctionne!
Quelques notes:
Je programme habituellement avec Qt, donc je suis habitué à avoir une boucle foreach et je n’ai jamais à traiter directement avec des itérateurs.
J'ai testé cela avec le compilateur de Qt (v 5.4.2) mais cela devrait fonctionner. Ceci est grossier pour plusieurs raisons, mais fait généralement ce que vous voudriez. Je ne tolère pas le codage comme celui-ci, mais il n'y a aucune raison que cela ne fonctionne pas tant que vous respectez la syntaxe.
#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;
}
Je suis venu ici parce que j'avais la même question, mais en C La meilleure chose que je sois sorti est
bool notTerminated = true;
for (int i = 0; i < 50 || (notTerminated = false); i++)
if (bar(i))
break;
if (! notTerminated)
baz();
Explication: le (notTerminated = false)
est une affectation qui renvoie toujours la valeur false, elle n’affectera jamais la condition et sera évaluée si la condition est vraie.
Il n’ya probablement pas de solution unique qui convient le mieux à tous les problèmes. Dans mon cas, une variable indicateur et une boucle for
basée sur une plage avec un spécificateur auto
fonctionnaient mieux. Voici un équivalent du code en question:
bool none = true;
for (auto i : foo) {
if (bar(i)) {
none = false;
break;
}
}
if (none) baz();
C'est moins typé que en utilisant des itérateurs . En particulier, si vous utilisez la boucle for
pour initialiser une variable, vous pouvez l’utiliser à la place du drapeau booléen.
Grâce à auto
, il vaut mieux que std::none_of
, si vous souhaitez intégrer la condition plutôt que d'appeler bar()
(et si vous n'utilisez pas C++ 14).
J'ai eu une situation où les deux conditions se sont produites, le code ressemblait à ceci:
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
}
}
Pas besoin d'itérateurs ici, car v
indique si break
s'est produit.
Utiliser std::none_of
nécessiterait de spécifier explicitement le type d'éléments support_edges[x]
dans les arguments d'une expression lambda.
Vous pouvez utiliser for-else presque comme en Python en définissant deux macros:
#define BREAK {CONTINUETOELSE = false; break;}
#define FORWITHELSE(x, y) {bool CONTINUETOELSE = true; x if(!CONTINUETOELSE){} y}
Maintenant, vous mettez les for
et les else
dans la macro FORWITHELSE
séparés par une virgule et utilisez BREAK
au lieu de break
Voici un exemple:
FORWITHELSE(
for(int i = 0; i < foo; i++){
if(bar(i)){
BREAK;
}
},
else{
baz();
}
)
Vous devez vous rappeler de deux choses: mettre une virgule avant la else
et utiliser BREAK
au lieu de break
.