Est-il possible de quitter a for time avant en C ++, si une condition de fin est atteinte?

StackOverflow https://stackoverflow.com/questions/416464

  •  03-07-2019
  •  | 
  •  

Question

Je veux savoir s'il est possible de terminer une boucle for en C ++ lorsqu'une condition de fin (différente du nombre correct d'itérations) est vérifiée. Par exemple:

for (int i = 0; i < maxi; ++i)
    for (int j = 0; j < maxj; ++j)
        // But if i == 4 < maxi AND j == 3 < maxj, 
        // then jump out of the two nested loops.

Je sais que cela est possible en Perl avec les prochains appels LABEL ou LABEL et les blocs étiquetés. Est-il possible de le faire en C ++ ou dois-je utiliser une boucle while?

Merci.

Était-ce utile?

La solution

Vous pouvez utiliser le mot-clé return: déplacez la boucle imbriquée dans un sous-programme, appelez le sous-programme pour exécuter les boucles imbriquées et "retour" du sous-programme pour quitter [toutes] les boucles.

Autres conseils

Malgré le " goto considéré comme dangereux " arguments, cela semble être le lieu idéal pour <=>. C'est essentiellement ce que vous faites en Perl. Sérieusement ... considérez les alternatives:

Variables d'état supplémentaires

for (int i=0; i<maxi; ++i) {
    bool leaveLoop = false;
    for (int j=0; j<maxj; ++j) {
        if (i == 4 && j == 3) {
            leaveLoop = true;
            break; // leave the inner loop
        }
    }
    if (leaveLoop) {
        break; // leave the outside loop
    }
}

Quitter par exception

try {
    for (int i=0; i<maxi; ++i) {
        for (int j=0; j<maxj; ++j) {
            if (i == 4 && j == 3) {
                throw leave_loop();
            }
        }
    }
} catch (leave_loop const&) {
}

Logique complexe

int j = 0;
for (int i=0; i<maxi && !(i==4 && j==3); ++i) {
    for (j=0; j<maxj && !(i==4 && j==3); ++j) {
        // inner loop
    }
}

<=>

for (int i=0; i<maxi; ++i) {
    for (int j=0; j<maxj; ++j) {
        if (i==4 && j==3) {
            goto leave_loop;
        }
    }
}
leave_loop:

Le dernier est-il moins clair? Je ne crois pas que ce soit le cas. Est-ce plus fragile? IMHO, les autres sont assez sujettes aux erreurs et fragiles par rapport à la version <=>. Désolé de me tenir sur la tribune, mais c’est quelque chose qui m’embête depuis un moment;)

La seule chose dont vous devez être conscient est que <=> et les exceptions sont assez similaires. Ils ouvrent tous les deux la possibilité de fuir des ressources et de ce qui ne les traite pas avec tant de soin.

Permettez-moi de le dire aussi énergiquement (mais poliment ;-) que possible: La construction for en langage C-like ne consiste pas à compter.

L'expression de test qui détermine s'il faut continuer ou non peut être quelque chose qui est pertinent pour l'objectif de la boucle; l'expression de mise à jour ne doit pas nécessairement être & "ajouter un à un compteur &";

for (int i = 0, j = 0; i < maxi && j < maxj && i != 4 && j != 3;) {
    if (j < maxj) {
        ++j;
    } else {
        j = 0;
        ++i;
    }
}

serait un moyen (assez arbitraire) de réécrire.

Le fait est que si l'établissement d'une condition est le point d'interaction, il est généralement possible d'écrire une boucle (en utilisant while ou <=>) de manière à énoncer plus explicitement la condition de continuation / terminée.

(Si vous pouviez publier une description de ce qui se passe réellement, il est probable que vous écrivez quelque chose qui n'a pas l'air aussi arbitraire que ce qui précède.)

Vous ne pouvez pas sortir de deux boucles avec une seule instruction de pause, mais vous pouvez utiliser un goto pour sauter depuis la boucle intérieure située juste à l'extérieur.

Si le goto est localisé, ce qui signifie qu'il y a moins de logique que sinon, je pense que c'est un code parfaitement acceptable. Avoir des variables de drapeau supplémentaires ou extraire la variable d'itérateur de la boucle interne afin de pouvoir la comparer dans la boucle externe ne facilite pas la compréhension du code à mon humble avis.

Parmi toutes les suggestions ci-dessus, j’éviterais d’utiliser le mécanisme try / catch car les exceptions devraient être réservées à des circonstances exceptionnelles et non à un flux de contrôle normal.

L'utilisation de deux pauses est acceptable si vous pouvez créer la deuxième condition de manière appropriée. Utiliser un booléen à cette fin serait également utile, et vous pouvez même le connecter à la condition de chaque boucle for. Par exemple:

bool exit_loops = false;
for (int a = 0; a < A && !exit_loops; ++a)
{
    for (int b = 0; b < B && !exit_loops; ++b)
    {
        if (some_condition) exit_loops = true;
    }
}

Toutefois, si vous utilisez plus de deux boucles, il serait peut-être plus approprié de les envelopper dans une fonction et d’utiliser simplement return pour quitter la fonction (ainsi que toutes les boucles). Ensuite, vous pouvez refactoriser le code de manière à éliminer toutes les boucles sauf une, soit en appelant une fonction pour exécuter le code de la boucle interne, etc.

Enfin, n’ayez pas peur d’utiliser un goto dans cette situation, il s’agit généralement d’une mauvaise programmation non structurée, mais dans certains cas (comme celle-ci), elle est très utile.

bool done = false;

for (int i = 0; i < maxi && !done; ++i)
    for (int j = 0; j < maxj && !done; ++j)
        if (i == 4 && i < maxi && j == 3 && j < maxj )
             done = true;
        else {
        }

Ou vous pouvez simplement aller. Ou pas: -)

Vous ne pouvez pas sortir comme ça en C / C ++:

for (...)
{
  for (...)
  {
    // from here...
  }
}
// ...to here

sans l'utilisation de goto. Vous avez besoin d'une construction telle que:

for (...)
{
  bool
    exit = false;

  for (...)
  {
    if (do_exit)
    {
      exit = true; // or set outer loop counter to end value
      break;
    }
  }
  if (exit)
  {
    break;
  }
}

Vous pouvez également utiliser throw et catch, mais ce n'est pas génial, car throw devrait vraiment être utilisé pour les exceptions et non pour le contrôle de flux.

Une méthode simple consiste à transformer la boucle interne en une fonction:

bool F ()
{
  if inner loop terminates, return false else return true
}

void G ()
{
  for (...)
  {
    if (!F ())
    {
      break;
    }
  }
}
for (int i = 0; i < maxi; ++i)
{
    int j = 0;
    for (j = 0; j < maxj; ++j)
    {
         if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
             break; // exit inner loop
    }
    if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
        break; // exit outer loop
}

Vous pouvez utiliser une déclaration goto . , mais c'est généralement considéré comme une mauvaise pratique.

Votre autre option est de faire quelque chose comme ça

int i;
int j = 0;
for (i = 0; i < maxi && !(i==4 && j==3); ++i)
    for (j = 0; j < maxj && !(i==4 && j==3); ++j)

La lecture du code ne doit pas ressembler à la lecture d’un livre de détective (qu’il faut toujours comprendre) ...

un exemple:

Java:

iterate_rows:
for (int i = 0; i < maxi; ++i)
{       
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            break iterate_rows;
        else
            continue iterate_rows;
    }   
}

Vous n'avez pas besoin de savoir ce que cassent iterate_rows, vous venez de le lire.

C ++:

//iterate_rows:
for (int i = 0; i < maxi; ++i)
{
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            goto break_iterate_rows;
        else
            goto continue_iterate_rows;
    }

continue_iterate_rows:;
}
break_iterate_rows:;

goto break_iterate_rows n'est qu'une version visible de break iterate_rows

Si vous limitez l'utilisation de goto et d'étiquettes à ce type de code, vous ne pourrez pas déterminer l'intention. En limitant votre utilisation de goto et d’étiquettes sur ce type de code, vous ne ferez que lire le code, et non l’analyser ou le comprendre. Vous ne serez pas accusé d'être un mauvais programmeur.

Et si vous limitez vraiment vos connaissances à ce type de code, vous pourrez développer l’habitude de ne pas avoir besoin de comprendre ce que ces maudits gotos font dans votre code. L’avantage supplémentaire est que vous n’avez pas besoin d’introduire des booléens et de les suivre (ce qui vous amène à détecter le code , ce qui le rend un peu illisible, ce qui va à l’encontre du but d'éviter les gotos)

P.S.

Associez ces étiquettes à des commentaires (avant la boucle). Lorsque vous lirez au-delà de ces lignes avec l'instruction goto, vous connaissez déjà l'intention de ces instructions.

J'ai quelques suggestions:

  1. jeter .... placez les deux boucles dans un " essayez {} " puis " catch " le " jeter " sur la condition.

  2. placez les deux boucles dans une méthode et retournez la condition.

  3. Goto n’est pas mauvais, c’est l’utilisation que les gens mettent aussi ... Vous pouvez utiliser " goto " il peut s'agir du code le plus clair, en particulier lors du traitement des erreurs. Je n'en ai pas utilisé depuis 20 ans.

Tony

J'ai toujours essayé de rester à l'écart des goto déclarations (elles ont toujours été méprisées à l'école et dans mon travail, pour une raison quelconque). Je voudrais utiliser quelque chose comme ce que Daemin a suggéré.

Vous pouvez utiliser des étiquettes, comme suit:

Outer:
for(...)
{
    Inner:
    for(...)
    {
    if(condition)
        {
        goto End;
        }
    }
}
End:

En Java, vous pouvez passer des étiquettes pour casser, je pense?

Modifier - Changé le goto en Fin plutôt qu'en Extérieur, mais je ne pense pas que la représentation négative soit justifiée. Cette réponse donne le moyen le plus simple de le faire.

Une autre raison de reconsidérer entièrement la construction for est que sa portée empêche l'accès aux variables contrôlées une fois la boucle terminée. La ou les valeurs de la ou des variables en cours de mutation dans la boucle peuvent être utiles pour diverses raisons (par exemple, pour distinguer le succès de l'échec dans une recherche), ce qui nécessiterait sinon des variables supplémentaires pour conserver ces informations après la sortie de la portée. Voici un petit exemple qui cherche une valeur a dans un tableau carré nommé target (en supposant que SIZE est différent de zéro, sinon aucune recherche n'est nécessaire!):

int i = 0;
int j = 0;
while (i < SIZE && a[i][j] != target) { // still in array but not at target
    if (SIZE <= ++j) {                  // fallen off the end of a row
        j = 0;
        ++i;
    }
}

Le code suivant peut utiliser i < SIZE pour déterminer si la valeur souhaitée a été localisée.

Un autre avantage de ce qui précède est la flexibilité. Supposons que nous apprenions maintenant que les valeurs dans les lignes de <=> sont croissantes, de sorte que le reste d'une ligne n'est pas pertinent si une valeur supérieure à <=> est rencontrée. Il est facile de savoir exactement quel changement apporter et où le faire. Étant donné que cette nouvelle information nous permet d’abandonner la ligne en cours, seule la décision interne est affectée et devient:

    if (target < a[i][j] || SIZE <= ++j) { // can't be in row or fallen off end
    ...

Je vois de plus en plus de nouveaux langages (en particulier ceux à orientation fonctionnelle) abandonnant l'ancien & "compter" & "; construction de boucle; c'est probablement une bonne chose, car cela nous incite à réfléchir à la signification de la boucle, plutôt que de simplement compter.

La meilleure façon que j'ai vue implique des macros et des options, mais c'est en fait tout à fait nice (le lien de publication commence par parler de Perl, mais le dernier paragraphe introduit les macros).

Il vous permet d'écrire du code tel que:

named (LOOPS) for (i=1; i<10; i++) {
    for (j=1; j<10; j++) {
        for (j=1; j<10; j++) {
            /* Process data[i][j][k] here */
            if (data[i][j][k] < threshold) break(LOOPS);
        }
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top