J'ai toujours su que goto
c'é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, goto
je 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 goto
si il est utilisé pour des cas aussi évidents et pertinents?
Réponses:
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
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:
où
$list_contains_ip
est votre seule variable d'état, ou implicitement: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:
Et quiconque devra déboguer ce code vous maudira, vous et vos enfants.
Dijkstra le dit comme ceci:
Et c'est pourquoi GOTO est considéré comme dangereux.
la source
$list_contains_ip = false;
déclaration semble mal placéeIl 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):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.GOTO
vous 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.la source
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.goto
avec 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 ungoto
.Comme d'autres l'ont dit, le problème ne vient pas de
goto
lui - même. le problème réside dans la manière dont les utilisateurs utilisentgoto
et rend difficile la compréhension et la maintenance du code.Supposons le fragment de code suivant:
Quelle est la valeur imprimée
i
? Quand est-il imprimé? Tant que vous n'avez pas rendu compte de chaque instance degoto label
votre 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
main
l’auteur, et l’auteur en utilisait une quinzaine degoto
branches dans les deux sens. C’était un mauvais code pour commencer, mais la présence de ceux-cigoto
le rendait bien pire. Il a fallu environ deux semaines à mon collègue pour comprendre le flux de contrôle. Mieux encore, ces modifications ontgoto
abouti à 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
goto
s 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
:if
oufor
ou d'unewhile
déclaration);goto
à la place d'une structure de contrôleIl 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:Maintenant, nous avons un problème - et si l’un des
malloc
appels é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
malloc
appel et prendre les mesures appropriées.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'ungoto
n'est pas immédiatement un signe de mauvaise pratique ou de mauvaise conception, c'est l'un des rares.la source
goto
mê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éboguergoto
Si elles ont été écrites selon ces règles.GOTO
, y compris les célèbres gotos assignés et arithmétiques de Fortran, si je me souviens bien.return
,break
,continue
Etthrow
/catch
sont 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!)
la source
return
break
continue
throw
catch
GOTO
est 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,
GOTO
vous 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
GOTO
voulaient les bannir complètement.Ironiquement, ce
GOTO
n'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
GOTO
peut être plus lisible." Les étudiants ne vont pas comprendre cela. Ils vont abuserGOTO
pour faire du code illisible.Au lieu de cela, vous dites "
GOTO
mauvais.GOTO
Mal.GOTO
Échec à l'examen". Les étudiants vont comprendre ça !la source
À 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:
Indépendamment de ce que construction de contrôle
foo
est (if
,while
, etc.), il n'y a que certaines commandes autorisées poura
,b
etc
.Vous pourriez avoir
a
-b
-c
, oua
-c
, ou mêmea
-b
-b
-b
-c
. Mais vous ne pourriez jamais avoirb
-c
oua
-b
-a
-c
.... sauf si vous avez
goto
.goto
(notamment en arrièregoto
) 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.goto
s 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.
la source
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.
la source
goto
peut 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.goto
apparaî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ùgoto
est utilisé pour implémenterfor
-else
.goto
est également utile pour des tâches telles que les tentatives d’exception, la restauration, les machines à états.Rien de mal avec les
goto
dé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
goto
pour sortir une boucle non imbriquée, ce que vous pouvez faire en utilisantbreak
. Dans ce cas, il vaut mieux utiliserbreak
.Mais si vous aviez un scénario de boucle imbriquée, utiliser
goto
serait plus élégant / plus simple. Par exemple: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).
la source
break
et l’ utilisationcontinue
d’un numéro (pour interrompre / continuer autant de couches de boucles) ou d’une étiquette préférable à l’utilisationgoto
de boucles imbriquées.Vrai. Ne t'en fais pas.
Vrai. Ne t'en fais pas.
Vrai. Ne t'en fais pas.
Vrai. Cependant, j'ai été ce codeur discipliné. J'ai vu ce qu'il advient du code au fil du temps.
Goto
commence 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.Goto
rend difficile de raisonner sur le code. Nous avons travaillé très dur créationwhile
,do while
,for
,for each
switch
,subroutines
,functions
et plus parce que faire ce genre de choses avecif
etgoto
est 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,if
commence à paraître un peu fragile.la source
Goto
vous permet d’abstraire un problème en en faisant un autre problème de code.If
vous permet de choisir cette abstraction. Il existe de meilleurs moyens d'abstraction et de meilleurs moyens de choisir l'abstraction. Polymorphisme pour un.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
la source