Quel est le problème avec goto quand il est utilisé pour ces cas évidents et pertinents?

40

J'ai toujours su que gotoc'était quelque chose de mauvais, enfermé dans un sous-sol quelque part pour ne jamais être vu pour de bon, mais je suis tombé sur un exemple de code qui est parfaitement logique à utiliser goto.

J'ai une adresse IP où je dois vérifier si se trouve dans une liste d'adresses IP, puis continuer avec le code, sinon émettez une exception.

<?php

$ip = '192.168.1.5';
$ips = [
    '192.168.1.3',
    '192.168.1.4',
    '192.168.1.5',
];

foreach ($ips as $i) {
    if ($ip === $i) {
        goto allowed;
    }
}

throw new Exception('Not allowed');

allowed:

...

Si je ne l'utilise pas, gotoje dois utiliser une variable comme

$allowed = false;

foreach ($ips as $i) {
    if ($ip === $i) {
        $allowed = true;
        break;
    }
}

if (!$allowed) {
    throw new Exception('Not allowed');
}

Ma question est la suivante: qu’en est-il gotosi il est utilisé pour des cas aussi évidents et pertinents?

php_nub_qq
la source
Les commentaires ne sont pas pour une discussion prolongée; cette conversation a été déplacée pour discuter .
maple_shaft

Réponses:

120

GOTO en soi n'est pas un problème immédiat, ce sont les machines à états implicites que les gens ont tendance à mettre en œuvre avec. Dans votre cas, vous voulez un code qui vérifie si l’adresse IP est dans la liste des adresses autorisées, donc

if (!contains($ips, $ip)) throw new Exception('Not allowed');

alors votre code veut vérifier une condition. L'algorithme pour implémenter cette vérification ne devrait pas être un problème ici, dans l'espace mental de votre programme principal, la vérification est atomique. Voilà comment il devrait être.

Mais si vous mettez le code qui vérifie dans votre programme principal, vous le perdez. Vous introduisez l'état mutable, soit explicitement:

$list_contains_ip = undef;        # STATE: we don't know yet

foreach ($ips as $i) {
  if ($ip === $i) {
      $list_contains_ip = true;   # STATE: positive
      break;
  }
                                  # STATE: we still don't know yet, huh?                                                          
                                  # Well, then...
  $list_contains_ip = false;      # STATE: negative
}

if (!$list_contains_ip) {
  throw new Exception('Not allowed');
}

$list_contains_ipest votre seule variable d'état, ou implicitement:

                             # STATE: unknown
foreach ($ips as $i) {       # What are we checking here anyway?
  if ($ip === $i) {
    goto allowed;            # STATE: positive
  }
                             # STATE: unknown
}
                             # guess this means STATE: negative
throw new Exception('Not allowed');

allowed:                     # Guess we jumped over the trap door

Comme vous le voyez, il existe une variable d'état non déclarée dans la construction GOTO. Ce n'est pas un problème en soi, mais ces variables d'état ressemblent à des cailloux: en porter un n'est pas difficile, en porter un sac plein vous fera transpirer. Votre code ne restera pas le même: le mois prochain, il vous sera demandé de différencier les adresses privées des adresses publiques. Le mois suivant, votre code devra prendre en charge les plages IP. L'année prochaine, quelqu'un vous demandera de prendre en charge les adresses IPv6. En peu de temps, votre code ressemblera à ceci:

if ($ip =~ /:/) goto IP_V6;
if ($ip =~ /\//) goto IP_RANGE;
if ($ip =~ /^10\./) goto IP_IS_PRIVATE;

foreach ($ips as $i) { ... }

IP_IS_PRIVATE:
   foreach ($ip_priv as $i) { ... }

IP_V6:
   foreach ($ipv6 as $i) { ... }

IP_RANGE:
   # i don't even want to know how you'd implement that

ALLOWED:
   # Wait, is this code even correct?
   # There seems to be a bug in here.

Et quiconque devra déboguer ce code vous maudira, vous et vos enfants.

Dijkstra le dit comme ceci:

L'utilisation incontrôlée de l'instruction go-to a pour conséquence immédiate qu'il devient extrêmement difficile de trouver un ensemble significatif de coordonnées permettant de décrire l'avancement du processus.

Et c'est pourquoi GOTO est considéré comme dangereux.

Wallenborn
la source
22
Cette $list_contains_ip = false;déclaration semble mal placée
Bergi
1
Un sac plein de cailloux est facile, j'ai un sac pratique pour les tenir. : p
whatsisname
13
@ Bergi: Vous avez raison. Cela montre à quel point il est facile de tout gâcher.
wallenborn
6
@whatsisname Ce sac est appelé une fonction / classe / paquet pour les contenir. utiliser des gotos, c'est comme les jongler et les jeter.
Falco
5
J'aime la citation de Dijkstra, je ne l'avais pas entendue dire, mais c'est un très bon point. Au lieu d’avoir un flux qui va du haut vers le bas, vous avez soudainement quelque chose qui peut parcourir la page au hasard. En résumé, c'est assez impressionnant.
Bill K
41

Il existe des cas d'utilisation légitimes pour GOTO. Par exemple, pour la gestion des erreurs et le nettoyage en C ou pour la mise en œuvre de certaines formes de machines à états. Mais ce n'est pas l'un de ces cas. Le deuxième exemple est plus lisible à mon humble avis, mais il serait encore plus lisible d’extraire la boucle dans une fonction distincte, puis de la renvoyer lorsque vous trouverez une correspondance. Encore mieux serait (en pseudocode, je ne connais pas la syntaxe exacte):

if (!in_array($ip, $ips)) throw new Exception('Not allowed');

Alors, quel est le problème avec GOTO? La programmation structurée utilise des fonctions et des structures de contrôle pour organiser le code afin que la structure syntaxique reflète la structure logique. Si quelque chose n'est exécuté que de manière conditionnelle, il apparaîtra dans un bloc d'instructions conditionnelles. Si quelque chose est exécuté dans une boucle, il apparaîtra dans un bloc de boucle. GOTOvous permet de contourner la structure syntaxique en effectuant des sauts arbitraires, ce qui rend le code beaucoup plus difficile à suivre.

Bien sûr, si vous n’avez pas d’autre choix GOTO, mais si le même effet peut être obtenu avec des fonctions et des structures de contrôle, il est préférable.

JacquesB
la source
5
@jameslarge Des it's easier to write good optimizing compilers for "structured" languages.soins à élaborer? En guise de contre-exemple, les gens de Rust ont introduit MIR , une représentation intermédiaire dans le compilateur qui remplace spécifiquement les boucles, les suites, les ruptures, etc. avec gotos, car il est plus simple de vérifier et d’optimiser.
8bittree
6
"Si vous n'avez pas d'autre choix, vous utilisez GOTO" Ce serait extrêmement rare. Honnêtement, je ne peux pas penser à un cas où vous n’avez pas d’autre choix, en dehors de restrictions totalement ridicules qui empêchent de refactoriser le code existant ou un non-sens similaire.
jpmc26
8
En outre, IMO, qui imite a gotoavec un nid de drapeaux et de conditionnels de rats (comme dans le deuxième exemple du PO) est beaucoup moins lisible que les juts utilisant un goto.
3
@ 8bittree: la programmation structurée est destinée au langage source, pas au langage cible du compilateur. Le langage cible des compilateurs natifs sont les instructions de la CPU, qui ne sont décidément pas "structurées" au sens du langage de programmation.
Robert Harvey
4
@ 8bittree La réponse à votre question sur MIR se trouve dans le lien que vous avez fourni: "Mais il est normal d’avoir une telle construction dans MIR, car nous savons qu’elle ne sera utilisée que de manière particulière, comme pour exprimer une boucle ou une pause. . " En d'autres termes, parce que goto n'est pas autorisé dans Rust, ils savent qu'il est structuré et qu'une version intermédiaire avec gotos l'est aussi. En fin de compte, le code doit être assimilé à des commandes machine. Ce que je comprends de votre lien, c’est qu’ils ajoutent simplement une étape supplémentaire dans la conversion du langage de haut niveau en langage de bas niveau.
JimmyJames
17

Comme d'autres l'ont dit, le problème ne vient pas de gotolui - même. le problème réside dans la manière dont les utilisateurs utilisent gotoet rend difficile la compréhension et la maintenance du code.

Supposons le fragment de code suivant:

       i = 4;
label: printf( "%d\n", i );

Quelle est la valeur imprimée i? Quand est-il imprimé? Tant que vous n'avez pas rendu compte de chaque instance de goto labelvotre fonction, vous ne pouvez pas savoir. La simple présence de cette étiquette détruit votre capacité à déboguer du code par simple inspection. Pour les petites fonctions avec une ou deux branches, ce n’est pas un problème. Pour les petites fonctions ...

Il y a bien longtemps, au début des années 90, on nous a donné une pile de code C pilotant un affichage graphique 3D et lui demandant de l'exécuter plus rapidement. Il ne s’agissait que d’environ 5000 lignes de code, mais l’ intégralité se trouvait dans mainl’auteur, et l’auteur en utilisait une quinzaine de gotobranches dans les deux sens. C’était un mauvais code pour commencer, mais la présence de ceux-ci gotole rendait bien pire. Il a fallu environ deux semaines à mon collègue pour comprendre le flux de contrôle. Mieux encore, ces modifications ont gotoabouti à un code si étroitement associé à lui-même que nous ne pouvions apporter aucun changement sans casser quelque chose.

Nous avons essayé de compiler avec une optimisation de niveau 1, et le compilateur a consommé toute la mémoire vive disponible, puis tous les échanges disponibles, puis nous avons paniqué le système (qui n'avait probablement rien à voir avec les gotos eux-mêmes, mais j'aime bien jeter cette anecdote là-bas).

Au final, nous avons donné deux options au client: laissez-nous réécrire tout à partir de zéro ou achetez du matériel plus rapide.

Ils ont acheté du matériel plus rapide.

Règles de Bode pour utiliser goto:

  1. Branche avant seulement;
  2. Ne pas contourner les structures de contrôle (c.-à-d. Ne pas se brancher dans le corps d'un ifou forou d'une whiledéclaration);
  3. Ne pas utiliser gotoà la place d'une structure de contrôle

Il y a des cas où a goto est la bonne réponse, mais ils sont rares (sortir d'une boucle profondément imbriquée est à peu près le seul endroit où je l'emploierais).

MODIFIER

En développant cette dernière déclaration, voici l’un des rares cas d’utilisation valides pour goto. Supposons que nous ayons la fonction suivante:

T ***myalloc( size_t N, size_t M, size_t P )
{
  size_t i, j, k;

  T ***arr = malloc( sizeof *arr * N );
  for ( i = 0; i < N; i ++ )
  {
    arr[i] = malloc( sizeof *arr[i] * M );
    for ( j = 0; j < M; j++ )
    {
      arr[i][j] = malloc( sizeof *arr[i][j] * P );
      for ( k = 0; k < P; k++ )
        arr[i][j][k] = initial_value();
    }
  }
  return arr;
}

Maintenant, nous avons un problème - et si l’un des mallocappels échouait au milieu? Aussi improbable que cela puisse être, nous ne voulons pas renvoyer un tableau partiellement alloué, nous ne voulons pas non plus renflouer la fonction avec une erreur; nous voulons nettoyer après nous-mêmes et désallouer toute mémoire partiellement allouée. Dans une langue qui lève une exception sur un mauvais allocation, c'est assez simple: vous écrivez simplement un gestionnaire d'exception pour libérer ce qui a déjà été alloué.

En C, vous n’avez pas de gestion structurée des exceptions; vous devez vérifier la valeur de retour de chaque mallocappel et prendre les mesures appropriées.

T ***myalloc( size_t N, size_t M, size_t P )
{
  size_t i, j, k;

  T ***arr = malloc( sizeof *arr * N );
  if ( arr )
  {
    for ( i = 0; i < N; i ++ )
    {
      if ( !(arr[i] = malloc( sizeof *arr[i] * M )) )
        goto cleanup_1;

      for ( j = 0; j < M; j++ )
      {
        if ( !(arr[i][j] = malloc( sizeof *arr[i][j] * P )) )
          goto cleanup_2;

        for ( k = 0; k < P; k++ )
          arr[i][j][k] = initial_value();
      }
    }
  }
  goto done;

  cleanup_2:
    // We failed while allocating arr[i][j]; clean up the previously allocated arr[i][j]
    while ( j-- )
      free( arr[i][j] );
    free( arr[i] );
    // fall through

  cleanup_1:
    // We failed while allocating arr[i]; free up all previously allocated arr[i][j]
    while ( i-- )
    {
      for ( j = 0; j < M; j++ )
        free( arr[i][j] );
      free( arr[i] );
    }

    free( arr );
    arr = NULL;

  done:
    return arr;
}

Peut-on faire cela sans utiliser goto? Bien sûr, nous pouvons le faire - cela nécessite juste un peu de comptabilité supplémentaire (et, en pratique, c'est le chemin que je prendrais). Mais si vous recherchez des endroits où l'utilisation d'un goton'est pas immédiatement un signe de mauvaise pratique ou de mauvaise conception, c'est l'un des rares.

John Bode
la source
J'aime ces règles. Je n'utiliserais toujours pas, gotomême en conformité avec ces règles - je ne l'emploierais pas du tout à moins que ce ne soit la seule forme de branchement disponible dans la langue - mais je vois que ce ne serait pas trop un cauchemar pour déboguer gotoSi elles ont été écrites selon ces règles.
Wildcard
si vous avez besoin de sortir d'une boucle profondément imbriquée, il vous suffit simplement de transformer cette boucle en une autre fonction et de la renvoyer
bunyaCloven
6
Comme quelqu'un l'a résumé une fois pour moi: "Ce n'est pas un problème essentiel, c'est un problème qui vient de". En tant que spécialiste en optimisation logicielle, je conviens que des flux de contrôle non structurés peuvent jeter des compilateurs (et des humains!) Pour une boucle. Une fois, j’ai passé des heures à déchiffrer une simple machine à états (quatre à cinq états) dans une fonction BLAS, qui utilisait diverses formes de GOTO, y compris les célèbres gotos assignés et arithmétiques de Fortran, si je me souviens bien.
Njuffa
@njuffa "En tant que spécialiste en optimisation logicielle, je conviens qu'un flux de contrôle non structuré peut générer des boucles pour les compilateurs (et les humains!)." - plus de détails? Je pensais que pratiquement tous les compilateurs d'optimisation utilisaient de nos jours SSA en IR, ce qui signifie qu'ils utilisaient un code de type goto.
Maciej Piechotka
@ MaciejPiechotka Je ne suis pas un ingénieur compilateur. Oui, les compilateurs modernes semblent tous utiliser la SSA dans leur représentation intermédiaire. Les compilateurs préfèrent les constructions de contrôle à entrée unique à sortie unique, vous ne savez pas comment l'utilisation de SSA joue dans cela? En observant l'utilisation du code machine généré et de l'utilisation des ressources du compilateur, et en discutant avec les ingénieurs du compilateur, un flux de contrôle non structuré nuit à l'optimisation des transformations de code, probablement en raison du problème de provenance. L'utilisation des ressources (temps, mémoire) peut avoir un impact négatif sur les performances du code si le compilateur décide de passer une optimisation trop coûteuse.
njuffa
12

return, break, continueEt throw/ catchsont tous essentiellement GOTO - ils ont tous le contrôle de transfert à un autre morceau de code et pourraient tous être mis en œuvre avec GOTO - en fait , je l' ai fait une fois dans un projet scolaire, un instructeur PASCAL a dit combien mieux Pascal était que basique à cause de la structure ... je devais donc être contraire ...

La chose la plus importante à propos du génie logiciel (je vais utiliser ce terme sur codage pour désigner une situation dans laquelle vous êtes payé par quelqu'un pour créer une base de code avec d'autres ingénieurs qui nécessite une amélioration et une maintenance continues) est de rendre le code lisible - Le faire faire quelque chose est presque secondaire. Votre code sera écrit une seule fois mais, dans la plupart des cas, les gens passeront des jours et des semaines à le réviser / réapprendre, à l'améliorer et à le corriger - et chaque fois qu'ils (ou vous-même) devrez recommencer à zéro et essayer de vous en souvenir. votre code.

La plupart des fonctionnalités ajoutées aux langues au fil des ans ont pour but de rendre les logiciels plus faciles à gérer, pas plus faciles à écrire (bien que certaines langues aillent dans cette direction - elles causent souvent des problèmes à long terme ...).

Par rapport aux déclarations de contrôle de flux similaires, les GOTO peuvent être presque aussi faciles à suivre à leur meilleur (un seul goto utilisé dans un cas comme vous le suggérez), et un cauchemar en cas de maltraitance - et sont très facilement maltraités ...

Ainsi, après avoir fait face à des cauchemars spaghettis pendant quelques années, nous avons simplement répondu "Non". En tant que communauté, nous n'allons pas accepter cela - trop de gens le gâchent si on leur laisse un peu de marge de manœuvre - c'est vraiment le seul problème avec eux. Vous pourriez les utiliser ... mais même si c'est le cas parfait, le prochain type supposera que vous êtes un programmeur terrible parce que vous ne comprenez pas l'histoire de la communauté.

Beaucoup d'autres structures ont été développées juste pour rendre votre code plus compréhensible: Fonctions, Objets, Portée, Encapsulation, Commentaires (!) ... ainsi que les modèles / processus plus importants tels que "DRY" (empêchant la duplication) et "YAGNI". (Réduire la généralisation / la complication du code) - tous importent uniquement pour que le gars NEXT lise votre code (Qui sera probablement vous - après avoir presque tout oublié de ce que vous avez fait!)

Bill K
la source
3
Je vote en faveur de la phrase suivante: "La chose la plus importante ... est de rendre le code lisible; le faire faire quelque chose est presque secondaire." Je dirais juste un peu différemment; ce n'est pas tant de rendre le code lisible que de le rendre simple . Mais de toute façon, il est tellement vrai que faire quelque chose est secondaire.
Wildcard
« , Et / sont tous essentiellement GOTO » Pas d' accord, l'ancien respectent les principes de la programmation structurée (bien que vous puissiez discuter des exceptions), allez à ne fait certainement pas. À la lumière de cette question, cette remarque n’a guère d’avantages. returnbreakcontinuethrowcatch
Eric
@eric Vous pouvez modéliser n'importe laquelle de ces déclarations avec des GOTO. Vous pouvez vous assurer que les GOTO sont tous d'accord avec les principes de programmation structurés: ils ont simplement une syntaxe différente et doivent, pour reproduire exactement la fonctionnalité, inclure d'autres opérations telles que "if". L'avantage des versions structurées réside dans le fait qu'elles sont limitées à la prise en charge de certaines cibles - vous SAVEZ lorsque vous voyez une continuation indiquant approximativement l'emplacement de la cible. La raison pour laquelle j'ai mentionné cela était de souligner que le problème était l'abus de pouvoir, et non le fait qu'il ne puisse pas être utilisé correctement.
Bill K
7

GOTOest un outil. Il peut être utilisé pour le bien ou pour le mal.

À l'époque maléfique, avec FORTRAN et BASIC, c'était presque le seul outil.

En regardant le code de ces jours, quand vous voyez un, GOTOvous devez comprendre pourquoi il est là. Cela peut faire partie d'un langage standard que vous pouvez comprendre rapidement ... ou d'une partie d'une structure de contrôle cauchemardesque qui n'aurait jamais dû l'être. Vous ne savez pas avant d'avoir regardé, et il est facile de se tromper.

Les gens voulaient quelque chose de mieux, et des structures de contrôle plus avancées ont été inventées. Celles-ci couvraient la plupart des cas d'utilisation et les personnes brûlées par des personnes malveillantes GOTOvoulaient les bannir complètement.

Ironiquement, ce GOTOn'est pas si grave quand c'est rare. Quand vous en voyez un, vous savez qu'il se passe quelque chose de spécial et il est facile de trouver l'étiquette correspondante, car il s'agit de la seule étiquette disponible à proximité.

Avance rapide à aujourd'hui. Vous êtes un conférencier enseignant la programmation. Vous pouvez dire "Dans la plupart des cas, vous devriez utiliser les nouvelles constructions avancées, mais dans certains cas, une simple GOTOpeut être plus lisible." Les étudiants ne vont pas comprendre cela. Ils vont abuser GOTOpour faire du code illisible.

Au lieu de cela, vous dites " GOTOmauvais. GOTOMal. GOTOÉchec à l'examen". Les étudiants vont comprendre ça !

Stig Hemmer
la source
4
Ceci est en fait vrai pour toutes les choses déconseillées. Globales. Noms de variables à une lettre. Code auto-modifiable. Eval. Même, je soupçonne, une entrée utilisateur non filtrée. Une fois que nous nous sommes préparés à éviter des choses telles que la peste, nous sommes alors dans une position appropriée pour évaluer si un certain usage d’eux, bien indiqué, peut être la solution correcte à un problème spécifique. Avant cela, nous ferions mieux de les traiter comme simplement bannis.
Dewi Morgan
4

À l'exception de goto, toutes les constructions de flux en PHP (et la plupart des langages) sont définies hiérarchiquement.

Imaginez du code examiné à travers des yeux louches:

a;
foo {
    b;
}
c;

Indépendamment de ce que construction de contrôle fooest ( if, while, etc.), il n'y a que certaines commandes autorisées pour a, betc .

Vous pourriez avoir a- b- c, ou a- c, ou même a- b- b- b- c. Mais vous ne pourriez jamais avoir b- cou a- b- a- c.

... sauf si vous avez goto.

$a = 1;
first:
echo 'a';
if ($a === 1) {
    echo 'b';
    $a = 2;
    goto first;
}
echo 'c'; 

goto (notamment en arrière goto ) peut être suffisamment gênant pour que le mieux soit de le laisser seul et d’utiliser des constructions de flux hiérarchisées et bloquées.

gotos ont leur place, mais surtout sous forme de micro-optimisations dans les langages de bas niveau. OMI, il n'y a pas de bonne place pour cela en PHP.


Pour votre information, l'exemple de code peut être écrit encore mieux que l'une de vos suggestions.

if(!in_array($ip, $ips, true)) {
    throw new Exception('Not allowed');
}
Paul Draper
la source
2

Dans les langages de bas niveau, GOTO est inévitable. Mais à haut niveau cela devrait être évité (dans le cas où la langue le supporte) car cela rend les programmes plus difficiles à lire .

Tout se résume à rendre le code plus difficile à lire.Les langages de haut niveau sont conçus pour rendre le code plus facile à lire que les langages de bas niveau tels que, par exemple, assembleur ou C.

GOTO ne provoque pas le réchauffement climatique ni la pauvreté dans le tiers monde. Cela rend le code plus difficile à lire.

La plupart des langues modernes ont des structures de contrôle qui rendent GOTO inutile. Certains comme Java ne l'ont même pas.

En fait, le terme code spaguetti vient de causes complexes, difficiles à suivre, dues à des structures de branchement non structurées.

Tulains Córdova
la source
6
gotopeut faire du code eaiser à lire. Lorsque vous créez des variables supplémentaires et des branches imbriquées par rapport à un seul saut, le code est beaucoup plus difficile à lire et à comprendre.
Matthew Whited
@ MatthewWhited Peut-être si vous avez un seul goto dans une méthode de dix lignes. Mais la plupart des méthodes ne comptent pas dix lignes et la plupart du temps, lorsque les utilisateurs utilisent GOTO, ils ne l'utilisent pas "pour la première fois".
Tulains Córdova
2
@MatthewWhited En fait, le terme code spaguetti vient d'un code compliqué, difficile à suivre et causé par des structures de branchement non structurées. Ou bien le code spaguetti est-il plus facile à rouge maintenant? Peut être. Le vinyle est de retour, pourquoi pas GOTO.
Tulains Córdova
3
@Tulains Je dois croire que de telles mauvaises choses existent parce que les gens continuent à se plaindre à ce moment-là, mais la plupart des fois que je vois gotoapparaître ne sont pas des exemples de code spaghetti compliqué, mais plutôt des choses assez simples, imitant souvent des schémas de contrôle dans des langages N'en avez pas la syntaxe: les deux exemples les plus courants sont la séparation de plusieurs boucles et l'exemple de l'OP où gotoest utilisé pour implémenter for- else.
1
Le code spaghetti non structuré provient de langages où les fonctions / méthodes n'existent pas. gotoest également utile pour des tâches telles que les tentatives d’exception, la restauration, les machines à états.
Matthew Whited
1

Rien de mal avec les gotodéclarations elles-mêmes. Les torts sont avec certaines des personnes qui utilisent de manière inappropriée la déclaration.

En plus de ce que JacquesB a dit (gestion des erreurs en C), vous utilisez gotopour sortir une boucle non imbriquée, ce que vous pouvez faire en utilisant break. Dans ce cas, il vaut mieux utiliser break.

Mais si vous aviez un scénario de boucle imbriquée, utiliser gotoserait plus élégant / plus simple. Par exemple:

$allowed = false;

foreach ($ips as $i) {
    foreach ($ips2 as $i2) {
        if ($ip === $i && $ip === $i2) {
            $allowed = true;
            goto out;
        }
    }
}

out:

if (!$allowed) {
    throw new Exception('Not allowed');
}

L'exemple ci-dessus n'a pas de sens dans votre problème spécifique, car vous n'avez pas besoin d'une boucle imbriquée. Mais j'espère que vous ne verrez que la boucle imbriquée.

Point positif: si votre liste d'adresses IP est petite, votre méthode est correcte. Mais si la liste s'allonge, sachez que votre approche présente une complexité asymptotique d'exécution maximale, à savoir O (n) . Au fur et à mesure que votre liste s'allonge, vous souhaiterez peut-être utiliser une méthode différente permettant d'atteindre O (log n) (telle qu'une structure arborescente) ou O (1) (une table de hachage sans collisions).

Homme des cavernes
la source
6
Je ne suis pas sûr à propos de PHP, mais de nombreuses langues prendront en charge l’utilisation breaket l’ utilisation continued’un numéro (pour interrompre / continuer autant de couches de boucles) ou d’une étiquette préférable à l’utilisation gotode boucles imbriquées.
8bittree
@ 8bittree bon point. Je ne savais pas que la pause de PHP était aussi sophistiquée. Je sais seulement que la pause de C n'est pas si élégante. La pause de Perl (ils l'appellent en dernier lieu ) ressemblait aussi beaucoup à celle du C (c'est-à-dire, elle n'était pas aussi sophistiquée que celle de PHP), mais elle est devenue trop élégante après un certain point. Je suppose que ma réponse devient de moins en moins utile à mesure que de plus en plus de langues introduisent des versions sophistiquées de break :)
homme des cavernes
3
une rupture avec une valeur de profondeur ne résoudrait pas le problème sans la variable supplémentaire. Une variable en elle-même qui crée un niveau supplémentaire de complexité.
Matthew Whited
Bien sûr, le fait que vous ayez des boucles imbriquées ne signifie pas que vous ayez besoin d'un goto, il existe d'autres moyens de gérer cette situation avec élégance.
NPSF3000
@ NPSF3000 Pourriez-vous s'il vous plaît offrir un exemple de boucle imbriquée que vous pouvez quitter sans utiliser d' instruction goto , tout en utilisant également un langage qui ne possède pas de version de rupture sophistiquée spécifiant la profondeur?
homme des cavernes
0

Avec goto je peux écrire du code plus rapide!

Vrai. Ne t'en fais pas.

Goto existe en assemblée! Ils l'appellent simplement jmp.

Vrai. Ne t'en fais pas.

Goto résout les problèmes plus simplement.

Vrai. Ne t'en fais pas.

Dans les mains d'un code de développeur discipliné utilisant goto, il peut être plus facile à lire.

Vrai. Cependant, j'ai été ce codeur discipliné. J'ai vu ce qu'il advient du code au fil du temps. Gotocommence bien. Puis l'envie de réutiliser le code s'installe. Assez vite, je me trouve à un point critique sans aucune idée de ce qui se passe même après avoir examiné l'état du programme. Gotorend difficile de raisonner sur le code. Nous avons travaillé très dur création while, do while, for, for each switch, subroutines, functionset plus parce que faire ce genre de choses avec ifet gotoest difficile sur le cerveau.

Donc non. Nous ne voulons pas regarder goto. Bien sûr, il est bien vivant dans le binaire mais nous n’avons pas besoin de voir cela dans les sources. En fait, ifcommence à paraître un peu fragile.

confits_orange
la source
1
"Goto résout les problèmes plus simplement." / "Vrai. Ne t'en fais pas." - Je détesterais voir votre code dans lequel vous évitez délibérément des solutions simples au profit de ce qui est le plus extensible. (Entendu parler de YAGNI?)
user253751
1
@Wildcard if et goto sont des moyens très simples de résoudre les problèmes, mieux résolus par d'autres moyens. Ils sont des fruits à portée de main. Ce n'est pas à propos de la langue particulière. Gotovous permet d’abstraire un problème en en faisant un autre problème de code. Ifvous permet de choisir cette abstraction. Il existe de meilleurs moyens d'abstraction et de meilleurs moyens de choisir l'abstraction. Polymorphisme pour un.
candied_orange
2
Bien que je sois d’accord avec vous, la réponse est mal formulée. "Vrai, ne t'en fais pas" n'est pas un argument convaincant. Je pense que votre message principal est le suivant: bien que goto séduise les personnes qui préfèrent utiliser des outils efficaces et puissants, cela entraîne une perte de temps étonnamment énorme, même lorsque des soins sont apportés.
Artelius
1
@artelius Le thème "Vrai, ne t'inquiète pas" n'a pas vocation à être convaincant. Cela a pour but d'illustrer les nombreux points que je vais concéder pour y parvenir et que je vais encore opposer. Par conséquent, discuter de ces points avec moi est inutile. Puisque ce n'est pas mon propos. Obtenir le point?
candied_orange
1
Je crois simplement qu'une bonne réponse ne fait pas qu'énoncer votre opinion, elle l'explique également et permet au lecteur de se faire son propre jugement. Dans l'état actuel des choses, il est difficile de comprendre pourquoi les avantages de goto ne l'emportent pas sur ses problèmes. La plupart d'entre nous ont passé des heures à arracher les cheveux en essayant de trouver des bugs qui n'impliquent pas de goto. Ce n'est pas la seule construction sur laquelle il est difficile de raisonner. Quelqu'un qui pose cette question est probablement beaucoup moins expérimenté et a besoin de mettre les choses en perspective.
Artelius
-1

Les langages d'assemblage ne comportent généralement que des sauts conditionnels / inconditionnels (l'équivalent de GOTO. Les implémentations plus anciennes de FORTRAN et BASIC ne contenaient aucune instruction de bloc de contrôle au-delà d'une itération comptée (la boucle DO), laissant tous les autres flux de contrôle aux IF et aux GOTO. La boucle DO en Ces langues étant terminées par une instruction numérotée, le code écrit pour ces langues pouvait être, et était souvent, difficile à suivre et sujet aux erreurs.

Pour souligner ce point, il y a la déclaration " VENUE DE " inventée facétieusement .

Il n’est pratiquement pas nécessaire d’utiliser GOTO dans des langages tels que C, C ++, C #, PASCAL, Java, etc. des constructions alternatives peuvent être utilisées qui seront presque certainement tout aussi efficaces et beaucoup plus faciles à maintenir. C'est vrai qu'un GOTO dans un fichier source ne posera pas de problème. Le problème est qu’il n’en faut pas beaucoup pour rendre une unité de code difficile à suivre et sujette aux erreurs de maintenance. C'est pourquoi la sagesse admise est d'éviter autant que possible GOTO.

Cet article de Wikipédia sur la déclaration de goto pourrait être utile

Zenilogix
la source