Dans le jeu d'instructions d'un processeur , une instruction de contrôle de flux modifie souvent le compteur de programme et peut être soit un branchement inconditionnel (ou saut), soit un branchement conditionnel. Une autre approche consiste à utiliser la prédication , qui active conditionnellement les instructions au lieu d'effectuer un branchement.
Un transfert de flux de contrôle asynchrone , tel qu'une interruption ou un signal, modifie le flux normal de contrôle vers un gestionnaire avant de rendre le contrôle à l'endroit où il a été interrompu.
Une méthode d'attaque logicielle consiste à détourner le flux d'exécution. Diverses techniques d'intégrité du flux de contrôle , telles que les canaris de pile , la protection contre les dépassements de tampon , les piles fantômes et la vérification des pointeurs de la table virtuelle , sont utilisées pour se défendre contre ces attaques.
la programmation structurée , qui limite la structure à la séquence, à la sélection et à l'itération basées sur une organisation par blocs .Séquence
Sequential execution is the most basic structure. Although not all code is sequential in nature, imperative code is.
Label
A label identifies a position in source code. Some control flow statements reference a label so that control jumps to the labeled line. Other than marking a position, a label has no other effect.
Some languages limit a label to a number which is sometimes called a line number, although that implies the inherent index of the line, not a label. None-the-less, such numeric labels are typically required to increment from top to bottom in a file even if not be sequential. For example, in BASIC:
);}Block
Most languages provide for organizing sequences of code as a block. When used with a control statement, the beginning of a block provides a jump target. For example, in the following C code (which uses curly braces to delimit a block), control jumps from line 1 to 4 if done is false.
Control);}voidbar(){foo();printf("Done");}Branch
Une instruction de branchement déplace le point d'exécution du point du code qui contient l'instruction au point spécifié par celle-ci.
Saut
Une instruction de saut transfère inconditionnellement le contrôle à un autre point du code et constitue la forme la plus élémentaire de contrôle du flux d'exécution.
Dans un langage de haut niveau, cela se traduit souvent par une instruction `goto` . Bien que le mot-clé puisse être en majuscules ou en minuscules, ou composé d'un ou deux mots selon le langage, son fonctionnement est similaire à : `goto`. Lorsqu'un contrôle atteint une instruction `goto`, il est exécuté directement à l'instruction qui suit l'étiquette indiquée. L'instruction `goto` a été jugée dangereuse par de nombreux informaticiens, dont Dijkstra .goto label
Branche conditionnelle
Une instruction conditionnelle modifie le comportement du programme en fonction de la valeur d'une expression booléenne . Voici quelques variantes courantes :
- si aller à
- Permet d'accéder à une étiquette en fonction d'une condition ; une instruction de programmation de haut niveau qui imite de près une instruction de code machine similaire.
- si-alors
- Au lieu d'être limitée à un saut, une instruction ou un bloc est exécuté si l'expression est vraie. Dans un langage ne possédant pas le
de Fortran disposaient d'une instruction arithmétique `if` (également appelée instruction `if` à trois voies) qui testait si une valeur numérique était négative, nulle ou positive. Cette instruction a été jugée obsolète dans Fortran 90 et supprimée dans Fortran 2018.
- Opérateur
- Certaines langues possèdent une forme d'opérateur , comme l' opérateur conditionnel ternaire .
- Quand et sauf
- Perl complète un style C
Smalltalk utilise des messages ifTrueet ifFalsepour implémenter des conditions, plutôt qu'une construction du langage.
Le code Pascal suivant illustre une structure conditionnelle simple de type « si-alors-sinon ». La syntaxe est similaire en Ada :
0 then writeln(\"yes\") else writeln(\"no\"); " si a > 0 alors écrire ( " oui " ) sinon écrire ( " non " ) ;
En C :
0) { puts(\"yes\"); } else { puts(\"no\"); } " if ( a > 0 ) { puts ( "oui" ); } else { puts ( "non" ); }
Dans bash :
sinon afficher "non" fiEn Python :
0: print(\"yes\") else: print(\"no\") " si a > 0 : afficher ( "oui" ) sinon : afficher ( "non" )
En Lisp :
"non" ))Branche à plusieurs voies
Une instruction de branchement multiple bascule l'exécution en fonction des valeurs correspondantes. Une action par défaut est généralement prévue si aucune correspondance n'est trouvée. L' instruction `switch` permet des optimisations du compilateur, telles que l'utilisation de tables de correspondance . Dans les langages dynamiques , les cas ne se limitent pas aux expressions constantes et peuvent inclure la correspondance de motifs , comme dans l' exemple de script shell à droite, où *)le cas par défaut est implémenté comme un glob correspondant à n'importe quelle chaîne de caractères. La logique de cas peut également être implémentée sous forme fonctionnelle, comme dans l'instruction SQL `switch` decode.
Le code Pascal suivant illustre une instruction switch relativement simple. Pascal utilise le Ada :
actionOnA; when 'x' => actionOnX; when 'y' | 'z' => actionOnYandZ; when others => actionOnNoMatch; end; " cas someChar is when ' a ' => actionOnA ; when ' x ' => actionOnX ; when ' y ' | ' z ' => actionOnYandZ ; when others => actionOnNoMatch ; end ;
En C :
Bash :Lisp :Fortran :types de boucles de programme de base indéfiniment . Une boucle à l'intérieur du corps de boucle est appelée boucle imbriquée . Il est possible de sortir prématurément d'une boucle grâce à une instruction `break`. Dans un langage de programmation fonctionnelle , tel que Haskell et Scheme , les processus récursifs et itératifs sont exprimés par des procédures récursives terminales au lieu de constructions de boucle syntaxiques.
Numérique
Scala propose les expressions `for` , qui généralisent les boucles contrôlées par collection et prennent également en charge d'autres usages, comme la programmation asynchrone . Haskell propose les expressions `do` et les compréhensions, qui offrent ensemble des fonctionnalités similaires aux expressions `for` de Scala.Infini
programmation informatique , une boucle infinie (ou boucle sans fin) est une séquence d'instructions qui, telle qu'elle est écrite, se poursuivra indéfiniment, à moins qu'une intervention extérieure ne survienne, comme la mise hors tension par un interrupteur ou le débranchement d'une prise. Cela peut être intentionnel.Il n'existe pas d'algorithme général permettant de déterminer si un programme informatique contient une boucle infinie ou non ; c'est le problème de l'arrêt .
Problème de boucle et demie , mais la seconde partie n'est exécutée que .
Ce problème a été identifié au moins depuis 1967 par Knuth, Wirth ayant suggéré de le résoudre par une sortie de boucle anticipée. Depuis les années 1990, il s'agit de la solution la plus couramment enseignée, utilisant une instruction break , comme dans :
boucle instructions if condition break instructions répéter
Une subtilité de cette solution réside dans le fait que la condition est l' inverse d'une condition while classique : réécrire while condition ... repeat avec une sortie au milieu nécessite d'inverser la condition : loop ... if not condition exit ... repeat . La structure de contrôle loop with test in the middle prend explicitement en charge le cas d'utilisation loop-an-a-half, sans inverser la condition.
Non structuré
Une boucle offre des critères d'achèvement structurés qui aboutissent soit à une nouvelle itération, soit à la poursuite de l'exécution après l'instruction de boucle. Cependant, de nombreux langages prennent également en charge diverses constructions de contrôle de flux non structurées.
- Première itération suivantePython permet l'exécution conditionnelle de code selon qu'une boucle a été interrompue prématurément (par une
breakinstruction `continue`) ou non, grâce à une clause `else`. Dans l'exemple de code Python suivant, cette elseclause est liée à l' forinstruction `continue` et non à l'instruction interne if. Les boucles `for` foret `for` de Python whileprennent en charge cette clause `else`, qui n'est exécutée que si la boucle n'a pas été interrompue prématurément.) break else : print ( "L'ensemble ne contient aucun nombre premier" )pauses à plusieurs niveaux
Certains langages permettent de sortir des boucles imbriquées ; en théorie, on parle alors de ruptures multiniveaux . Un exemple courant est la recherche dans une table multidimensionnelle. Ceci peut se faire soit par des ruptures multiniveaux (sortie de N niveaux), comme dans bash et PHP , soit par des ruptures étiquetées (sortie et poursuite à un niveau donné), comme dans Ada, Go, Java, Rust et Perl . D'autres alternatives aux ruptures multiniveaux existent : les ruptures simples, associées à une variable d'état testée pour sortir à un autre niveau ; les exceptions, interceptées au niveau de sortie ; le placement des boucles imbriquées dans une fonction et l'utilisation de `return` pour interrompre la boucle ; ou encore l'utilisation d'une étiquette et d'une instruction `goto`. Ni C ni C++ ne prennent actuellement en charge les ruptures multiniveaux ni les boucles nommées ; l'alternative habituelle consiste donc à utiliser `goto` pour implémenter une rupture étiquetée. Toutefois, l’intégration de cette fonctionnalité a été proposée, et ajoutée à C2Y., suivant la syntaxe Java. Python ne dispose pas d’instructions break ou continue à plusieurs niveaux ; cette fonctionnalité a été proposée dans la PEP 3136 , mais rejetée au motif que la complexité supplémentaire ne justifiait pas les rares cas d’utilisation légitime.
La notion de ruptures multiniveaux présente un certain intérêt en informatique théorique , car elle est à l'origine de ce que l'on appelle aujourd'hui la hiérarchie de Kosaraju . En 1973, S. Rao Kosaraju a affiné le théorème du programme structuré en démontrant qu'il est possible d'éviter l'ajout de variables supplémentaires en programmation structurée, pourvu que des ruptures multiniveaux de profondeur arbitraire issues de boucles soient autorisées. De plus, Kosaraju a prouvé l'existence d'une hiérarchie stricte de programmes : pour tout entier n , il existe un programme contenant une rupture multiniveau de profondeur n qui ne peut être réécrit comme un programme comportant des ruptures multiniveaux de profondeur inférieure à n sans introduire de variables supplémentaires.
Dans son manuel de 2004, David Watt utilise la notion de séquenceur de Tennent pour expliquer la similarité entre les interruptions multiniveaux et les instructions de retour. Watt note qu'une classe de séquenceurs appelés séquenceurs d'échappement , définis comme « séquenceur qui met fin à l'exécution d'une commande ou d'une procédure textuellement englobante », englobe à la fois les interruptions de boucle (y compris les interruptions multiniveaux) et les instructions de retour. Toutefois, dans leur implémentation courante, les séquenceurs de retour peuvent également contenir une valeur de retour, contrairement aux séquenceurs d'interruption, tels qu'implémentés dans les langages modernes.
Test intermédiaire
La structure suivante a été proposée par Dahl en 1972 :
boucle boucle xxx1 lire(char); tant que test; tant que non à la fin du fichier; xxx2 écrire(char); répéter ; répéter ;
Cette construction peut être vue comme une boucle `do` avec une condition `while` au milieu, ce qui permet une logique de boucle et demie claire. De plus, en omettant certains composants, cette construction unique peut remplacer plusieurs constructions dans la plupart des langages de programmation. Si `xxx1` est omis, on obtient une boucle avec la condition en début (une boucle `while` classique ). Si `xxx2` est omis, on obtient une boucle avec la condition en fin, équivalente à une boucle `do while` dans de nombreux langages. Si `while` est omis, on obtient une boucle infinie. Cette construction permet également de conserver la même polarité de la condition, même lorsqu'elle est au milieu, contrairement à la sortie anticipée qui nécessite d'inverser la polarité (en ajoutant ` not` ), fonctionnant comme `until` au lieu de `while` .
Cette structure n'est pas largement prise en charge, la plupart des langages utilisant plutôt if ... break pour une sortie anticipée conditionnelle.
Ceci est pris en charge par certains langages, tels que Forth , où la syntaxe est BEGIN ... WHILE ... REPEAT, et les langages de script shell Bourne shell ( sh) et bash , où la syntaxe est while ... do ... done ou until ... do ... done , comme :
opérateur virgule , et dans d'autres langages avec des constructions similaires , qui permettent d'insérer de force une liste d'instructions dans la condition while :Les variantes et les invariants de boucle sont utilisés pour exprimer la correction des boucles. Concrètement, une variante de boucle est une expression entière dont la valeur initiale est non négative. La valeur de cette variante doit diminuer à chaque itération de la boucle, mais ne doit jamais devenir négative pendant son exécution. Les variantes de boucle permettent de garantir la terminaison des boucles.
Un invariant de boucle est une assertion qui doit être vraie avant la première itération et rester vraie après chaque itération. Cela signifie que lorsqu'une boucle se termine correctement, la condition de sortie et l'invariant de boucle sont tous deux satisfaits. Les invariants de boucle servent à contrôler certaines propriétés d'une boucle au cours des itérations successives.
Certains langages de programmation, comme Eiffel, intègrent une prise en charge native des variantes et des invariants de boucles. Dans d'autres cas, cette prise en charge est un ajout, comme la spécification du langage de modélisation Java pour les instructions de boucle en Java .
Sous-langage de boucle
Certains dialectes Lisp proposent un sous-langage étendu pour la description des boucles. On en trouve un exemple ancien dans Conversional Lisp d' Interlisp . Common Lisp fournit une macro Loop qui implémente un tel sous-langage.
Tableau de correspondance des systèmes de boucles
langage de programmation Conditionnel Boucle Sortie anticipée continuation de la boucle Refaire Réessayer Installations de correction Commencer Milieu Fin Numérique Collection Général Infini [1] Variante Invariant Ada APL [3] C [2] [3] [3] C++ [2] [9] [3] [3] C# [2] [3] [3] COBOL [15] [14] Common Lisp [16] D [14] Eiffel [10] [10] [11] [13] Fa# [6] FORTRAN 77 Fortran 90 Fortran 95 et versions ultérieures Aller Haskell [6] Java [2] [12] [12] JavaScript [2] Kotlin OCaml [6] [17] [5] [17] PHP [2] [5] [4] Perl [2] [5] Python [5] [6] [6] QB64 Rebol [7] [8] [6] Rubis [6] [6] Rouiller [5] ML standard [6] Rapide Visual Basic .NET PowerShell [2] Zig La boucle « a » n'est pas considérée comme une boucle infinie à cet effet, car il ne s'agit pas d'une structure de langage dédiée.while (true)La boucle de C est une construction de boucle générale, et non spécifiquement une boucle de comptage, bien qu'elle soit souvent utilisée à cette fin .for (init; test; increment) a b c Des ruptures profondes peuvent être réalisées en APL, C, C++ et C# grâce à l'utilisation d'étiquettes et de goto. L' itération sur les objets a étéajoutéedans PHP 5. a b c d e f Une boucle de comptage peut être simulée en itérant sur une liste incrémentale ou un générateur, par exemple, celui de Python.range() a b c d e Les ruptures profondes peuvent être réalisées grâce à la gestion des exceptions. Il n'y a pas de construction spéciale, puisque lawhilefonction peut être utilisée à cette fin. Il n'existe pas de construction spéciale, mais les utilisateurs peuvent définir des fonctions de boucle générales. La norme C++11 a introduit l'itérationparplage. Dans laSTL, il existe unestd::for_eachmodèlequi permet d'itérer surles conteneurset d'appeler unefonction unairepour chaque élément. Cette fonctionnalité peut également être implémentée sous formede macrosur ces conteneurs. Une boucle numérique est effectuée par itération sur un intervalle entier ; sortie anticipée en incluant une condition de sortie supplémentaire. Eiffel prend en charge un mot réservéretry, mais celui-ci est utilisé dansla gestion des exceptionset non dans le contrôle des boucles. a Nécessitele langage de spécification d'interface comportementaleJava Modeling Language a) Les variantes de boucle doivent être des entiers ; les variantes transfinies ne sont pas prises en charge.Eiffel : Pourquoi les variantes de boucle sont des entiers Un objet D prend en charge les collections infinies et la possibilité d'itérer sur ces collections. Cela ne nécessite aucune construction particulière. Des ruptures profondes peuvent être obtenues en utilisantGO TOet en procédant. Common Lisp est antérieur au concept de type de collection générique. La boucle générale d'Odin prend en charge les raccourcis syntaxiques pour les boucles conditionnelles et les boucles infinies.for Non local
De nombreux langages de programmation, notamment ceux qui privilégient les styles de programmation dynamiques, offrent des mécanismes de contrôle de flux non local permettant de passer de l'exécution courante à un point prédéfini . Quelques exemples notables suivent.
Gestion des conditions
Les premiers compilateurs Fortran prenaient en charge des instructions pour gérer les conditions exceptionnelles, notamment `true` IF ACCUMULATOR OVERFLOW, IF QUOTIENT OVERFLOW`false` et IF DIVIDE CHECK`false`. Par souci d'indépendance vis-à-vis de la machine, ces instructions n'ont pas été incluses dans FORTRAN IV ni dans la norme Fortran 66. Cependant, depuis Fortran 2003, il est possible de tester les problèmes numériques via des appels aux fonctions du IEEE_EXCEPTIONSmodule `numérique`.
PL/I possède environ 22 conditions standard (par exemple, ZERODIVIDE SUBSCRIPTRANGE ENDFILE) qui peuvent être déclenchées et interceptées par : l'action de condition ON ; Les programmeurs peuvent également définir et utiliser leurs propres conditions nommées.
Comme pour l'instruction if non structurée , une seule instruction peut être spécifiée ; dans de nombreux cas, une instruction GOTO est donc nécessaire pour déterminer où le flux de contrôle doit reprendre.
Malheureusement, certaines implémentations présentaient une surcharge importante en termes d'espace et de temps (notamment SUBSCRIPTRANGE), si bien que de nombreux programmeurs ont essayé d'éviter d'utiliser des conditions.
Un exemple typique de syntaxe :
Sous condition, allez à l' étiquette
Gestion des exceptions
De nombreux langages modernes prennent en charge nativement la gestion des exceptions . Généralement, le flux de contrôle des exceptions commence par la levée d'une exception. Le contrôle est alors transmis au gestionnaire d'exceptions le plus interne de la pile d'appels . Si ce gestionnaire traite l'exception, le flux de contrôle reprend son cours normal. Sinon, le contrôle se propage vers les gestionnaires externes jusqu'à ce que l'un d'eux traite l'exception ou que le programme atteigne la portée la plus externe et se termine. À mesure que le contrôle se propage vers les gestionnaires externes, les opérations qui se produiraient normalement, telles que le dépilement de la pile d'appels, sont gérées automatiquement.
Le code C++ suivant illustre la gestion structurée des exceptions. Si une exception est propagée lors de l'exécution Exception détectée : {}" , e.what ( ));} attraper (...) {std :: println ( "Erreur inconnue" );}faire la prochaine chose ();De nombreux langages utilisent les mots-clés C++ `catch` AppleScript intègre des espaces réservés dans sa syntaxe pour extraire des informations sur l'exception, comme illustré dans le code AppleScript suivant.
) alors afficher la boîte de dialogue "Vous ne devez pas faire cela" fin de la tentativeDans de nombreux langages (dont Object Pascal, D, Java, C# et Python), une , FileMode . Create ); return ProcessStuff ( stream ); } finally { if ( stream != null ) { stream . Close (); } }Ce schéma étant fréquent, C# fournit l' , FileMode . Create )) { return ProcessStuff ( stream ); }
Continuation
informatique , une continuation est une représentation abstraite de l'état de contrôle d'un programme . Elle concrétise cet état, c'est-à-dire qu'il s'agit d'une structure de données représentant le processus de calcul à un instant donné de son exécution. Cette structure est accessible par le langage de programmation, contrairement aux structures de données masquées dans l' environnement d'exécution . Les continuations sont utiles pour implémenter d'autres mécanismes de contrôle, tels que les exceptions , les générateurs , les coroutines , etc.La « continuation courante » ou « suite de l'étape de calcul » est la suite qui, du point de vue de l'exécution du programme, serait obtenue à partir du point actuel de son exécution. Le terme « continuations » peut également désigner les continuations de première classe, c'est-à-dire les constructions qui permettent à un langage de programmation de sauvegarder l'état d'exécution à tout moment et d'y revenir ultérieurement, éventuellement à plusieurs reprises.
Générateur
informatique , un générateur est une routine permettant de contrôler le comportement d'itération d'une boucle . Tous les générateurs sont également des itérateurs . Un générateur est très similaire à une fonction renvoyant un tableau : il possède des paramètres , peut être appelé et génère une séquence de valeurs. Cependant, au lieu de construire un tableau contenant toutes les valeurs et de le renvoyer simultanément, un générateur produit les valeurs une à une, ce qui nécessite moins de mémoire et permet à l'appelant de commencer immédiatement le traitement des premières valeurs. En résumé, un générateur ressemble à une fonction mais se comporte comme un itérateur .Les générateurs peuvent être implémentés en termes de constructions de flux de contrôle plus expressives, telles que les coroutines ou les continuations de première classe . Les générateurs, également connus sous le nom de semi-coroutines, sont un cas particulier (et plus faible que) les coroutines, en ce sens qu'ils rendent toujours le contrôle à l'appelant (lorsqu'ils renvoient une valeur), plutôt que de spécifier une coroutine vers laquelle sauter ; voir la comparaison des coroutines avec les générateurs .
Coroutine
Les coroutines sont des composants de programme informatique pouvant être suspendus et repris — une généralisation des sous-programmes — pour le multitâche coopératif . Elles sont particulièrement adaptées à la mise en œuvre de composants de programme courants tels que les tâches coopératives , les exceptions , les boucles d'événements , les itérateurs , les listes infinies et les tubes .Elles ont été décrites comme des « fonctions dont vous pouvez suspendre l’exécution ».
Melvin Conway a inventé le terme coroutine en 1958 lorsqu'il l'a appliqué à la construction d'un programme d'assemblage . La première explication publiée de la coroutine est apparue plus tard, en 1963.
COMEFROM
programmation , l'instruction COMEFROM est une instruction de contrôle de flux qui provoque un saut à l'instruction suivante lorsque le contrôle atteint le point spécifié par son argument COMEFROM. Cette instruction est l'inverse de l'instruction goto et relève davantage de la plaisanterie que de la science informatique sérieuse . Le point de saut spécifié est souvent identifié par une étiquette . Par exemple, COMEFROM xl'instruction COMEFROM indique que lorsque le contrôle atteint l'étiquette x, il reprend son exécution à l'instruction suivant le COMEFROM.Une différence majeure avec l'instruction `goto` réside dans le fait que `goto` dépend de la structure locale du code, tandis que `COMEFROM` dépend de la structure globale. Une instruction `goto` transfère le contrôle lorsque celui-ci l'atteint, tandis que `COMEFROM` exige que le processeur (c'est-à-dire l'interpréteur) recherche les instructions `COMEFROM` afin que, lorsque le contrôle atteint l'un des points spécifiés, le processeur puisse effectuer le saut. La logique qui en résulte est souvent difficile à comprendre, car aucun indice n'indique à proximité d'un point de saut que le contrôle va effectivement sauter. Il est nécessaire d'étudier l'intégralité du programme pour vérifier si des instructions `COMEFROM` font référence à ce point.
La sémantique d'une instruction COMEFROM varie selon le langage de programmation . Dans certains langages, le saut a lieu avant l'exécution de l'instruction au point spécifié, et dans d'autres, après. Selon le langage, plusieurs instructions COMEFROM référençant le même point peuvent être invalides, non déterministes, exécutées dans un ordre particulier, ou induire un traitement parallèle ou concurrent, comme illustré dans Threaded Intercal .
L'instruction COMEFROM est apparue initialement dans des listes d'instructions humoristiques en langage assembleur (sous la forme « CMFRM »). Elle a été développée dans un article de Datamation par R. Lawrence Clark en 1973 , en réponse à la lettre d' Edsger Dijkstra intitulée « Go To Statement Considered Harmful ». COMEFROM a finalement été implémentée dans la variante C-INTERCAL du langage de programmation ésotérique INTERCAL, ainsi que dans l'instruction encore plus obscure « computed COMEFROM ». Des propositions ont également été faites en Fortran pour « assigned COME FROM » et une instruction « DONT » (pour compléter la boucle « DO » existante).
Sortie anticipée de boucle imbriquée basée sur un événement
Le modèle de Zahn a été proposé en 1974, et discuté dans la gestion des exceptions , et les exceptions ou des constructions similaires sont utilisées à cette fin dans de nombreux langages.
L'exemple simple suivant consiste à rechercher un élément particulier dans un tableau bidimensionnel.
sortir lorsqu'il est trouvé ou manquant ; pour I := 1 à N faire pour J := 1 à M faire si table[I,J] = cible alors trouvé ; manquant; sorties trouvé : imprimer ("l'élément est dans le tableau"); manquant : impression ("l'élément n'est pas dans le tableau") ; fin de sortie ;
Branch
Une instruction de branchement déplace le point d'exécution du point du code qui contient l'instruction au point spécifié par celle-ci.
Saut
Une instruction de saut transfère inconditionnellement le contrôle à un autre point du code et constitue la forme la plus élémentaire de contrôle du flux d'exécution.
Dans un langage de haut niveau, cela se traduit souvent par une instruction `goto` . Bien que le mot-clé puisse être en majuscules ou en minuscules, ou composé d'un ou deux mots selon le langage, son fonctionnement est similaire à : `goto`. Lorsqu'un contrôle atteint une instruction `goto`, il est exécuté directement à l'instruction qui suit l'étiquette indiquée. L'instruction `goto` a été jugée dangereuse par de nombreux informaticiens, dont Dijkstra .goto label
Branche conditionnelle
Une instruction conditionnelle modifie le comportement du programme en fonction de la valeur d'une expression booléenne . Voici quelques variantes courantes :
- si aller à
- Permet d'accéder à une étiquette en fonction d'une condition ; une instruction de programmation de haut niveau qui imite de près une instruction de code machine similaire.
- si-alors
- Au lieu d'être limitée à un saut, une instruction ou un bloc est exécuté si l'expression est vraie. Dans un langage ne possédant pas le
de Fortran disposaient d'une instruction arithmétique `if` (également appelée instruction `if` à trois voies) qui testait si une valeur numérique était négative, nulle ou positive. Cette instruction a été jugée obsolète dans Fortran 90 et supprimée dans Fortran 2018.
- Opérateur
- Certaines langues possèdent une forme d'opérateur , comme l' opérateur conditionnel ternaire .
- Quand et sauf
- Perl complète un style C
Smalltalk utilise des messagesifTrueetifFalsepour implémenter des conditions, plutôt qu'une construction du langage.
Le code Pascal suivant illustre une structure conditionnelle simple de type « si-alors-sinon ». La syntaxe est similaire en Ada :
si a > 0 alors écrire ( " oui " ) sinon écrire ( " non " ) ;
En C :
if ( a > 0 ) { puts ( "oui" ); } else { puts ( "non" ); }
Dans bash :
En Python :
si a > 0 : afficher ( "oui" ) sinon : afficher ( "non" )
En Lisp :
Branche à plusieurs voies
Une instruction de branchement multiple bascule l'exécution en fonction des valeurs correspondantes. Une action par défaut est généralement prévue si aucune correspondance n'est trouvée. L' instruction `switch` permet des optimisations du compilateur, telles que l'utilisation de tables de correspondance . Dans les langages dynamiques , les cas ne se limitent pas aux expressions constantes et peuvent inclure la correspondance de motifs , comme dans l' exemple de script shell à droite, où *)le cas par défaut est implémenté comme un glob correspondant à n'importe quelle chaîne de caractères. La logique de cas peut également être implémentée sous forme fonctionnelle, comme dans l'instruction SQL `switch` decode.
Le code Pascal suivant illustre une instruction switch relativement simple. Pascal utilise le Ada :
cas someChar is when ' a ' => actionOnA ; when ' x ' => actionOnX ; when ' y ' | ' z ' => actionOnYandZ ; when others => actionOnNoMatch ; end ;
En C :
Dans un langage de programmation fonctionnelle , tel que Haskell et Scheme , les processus récursifs et itératifs sont exprimés par des procédures récursives terminales au lieu de constructions de boucle syntaxiques.
Numérique
Infini
Il n'existe pas d'algorithme général permettant de déterminer si un programme informatique contient une boucle infinie ou non ; c'est le problème de l'arrêt .
Problème de boucle et demie , mais la seconde partie n'est exécutée que .
Ce problème a été identifié au moins depuis 1967 par Knuth, Wirth ayant suggéré de le résoudre par une sortie de boucle anticipée. Depuis les années 1990, il s'agit de la solution la plus couramment enseignée, utilisant une instruction break , comme dans :
boucle instructions if condition break instructions répéter
Une subtilité de cette solution réside dans le fait que la condition est l' inverse d'une condition while classique : réécrire while condition ... repeat avec une sortie au milieu nécessite d'inverser la condition : loop ... if not condition exit ... repeat . La structure de contrôle loop with test in the middle prend explicitement en charge le cas d'utilisation loop-an-a-half, sans inverser la condition.
Non structuré
Une boucle offre des critères d'achèvement structurés qui aboutissent soit à une nouvelle itération, soit à la poursuite de l'exécution après l'instruction de boucle. Cependant, de nombreux langages prennent également en charge diverses constructions de contrôle de flux non structurées.
- Première itération suivantePython permet l'exécution conditionnelle de code selon qu'une boucle a été interrompue prématurément (par une
breakinstruction `continue`) ou non, grâce à une clause `else`. Dans l'exemple de code Python suivant, cetteelseclause est liée à l'forinstruction `continue` et non à l'instruction interneif. Les boucles `for`foret `for` de Pythonwhileprennent en charge cette clause `else`, qui n'est exécutée que si la boucle n'a pas été interrompue prématurément.) break else : print ( "L'ensemble ne contient aucun nombre premier" )pauses à plusieurs niveaux
Certains langages permettent de sortir des boucles imbriquées ; en théorie, on parle alors de ruptures multiniveaux . Un exemple courant est la recherche dans une table multidimensionnelle. Ceci peut se faire soit par des ruptures multiniveaux (sortie de N niveaux), comme dans bash et PHP , soit par des ruptures étiquetées (sortie et poursuite à un niveau donné), comme dans Ada, Go, Java, Rust et Perl . D'autres alternatives aux ruptures multiniveaux existent : les ruptures simples, associées à une variable d'état testée pour sortir à un autre niveau ; les exceptions, interceptées au niveau de sortie ; le placement des boucles imbriquées dans une fonction et l'utilisation de `return` pour interrompre la boucle ; ou encore l'utilisation d'une étiquette et d'une instruction `goto`. Ni C ni C++ ne prennent actuellement en charge les ruptures multiniveaux ni les boucles nommées ; l'alternative habituelle consiste donc à utiliser `goto` pour implémenter une rupture étiquetée. Toutefois, l’intégration de cette fonctionnalité a été proposée, et ajoutée à C2Y., suivant la syntaxe Java. Python ne dispose pas d’instructions break ou continue à plusieurs niveaux ; cette fonctionnalité a été proposée dans la PEP 3136 , mais rejetée au motif que la complexité supplémentaire ne justifiait pas les rares cas d’utilisation légitime.
La notion de ruptures multiniveaux présente un certain intérêt en informatique théorique , car elle est à l'origine de ce que l'on appelle aujourd'hui la hiérarchie de Kosaraju . En 1973, S. Rao Kosaraju a affiné le théorème du programme structuré en démontrant qu'il est possible d'éviter l'ajout de variables supplémentaires en programmation structurée, pourvu que des ruptures multiniveaux de profondeur arbitraire issues de boucles soient autorisées. De plus, Kosaraju a prouvé l'existence d'une hiérarchie stricte de programmes : pour tout entier n , il existe un programme contenant une rupture multiniveau de profondeur n qui ne peut être réécrit comme un programme comportant des ruptures multiniveaux de profondeur inférieure à n sans introduire de variables supplémentaires.
Dans son manuel de 2004, David Watt utilise la notion de séquenceur de Tennent pour expliquer la similarité entre les interruptions multiniveaux et les instructions de retour. Watt note qu'une classe de séquenceurs appelés séquenceurs d'échappement , définis comme « séquenceur qui met fin à l'exécution d'une commande ou d'une procédure textuellement englobante », englobe à la fois les interruptions de boucle (y compris les interruptions multiniveaux) et les instructions de retour. Toutefois, dans leur implémentation courante, les séquenceurs de retour peuvent également contenir une valeur de retour, contrairement aux séquenceurs d'interruption, tels qu'implémentés dans les langages modernes.
Test intermédiaire
La structure suivante a été proposée par Dahl en 1972 :
boucle boucle xxx1 lire(char); tant que test; tant que non à la fin du fichier; xxx2 écrire(char); répéter ; répéter ;
Cette construction peut être vue comme une boucle `do` avec une condition `while` au milieu, ce qui permet une logique de boucle et demie claire. De plus, en omettant certains composants, cette construction unique peut remplacer plusieurs constructions dans la plupart des langages de programmation. Si `xxx1` est omis, on obtient une boucle avec la condition en début (une boucle `while` classique ). Si `xxx2` est omis, on obtient une boucle avec la condition en fin, équivalente à une boucle `do while` dans de nombreux langages. Si `while` est omis, on obtient une boucle infinie. Cette construction permet également de conserver la même polarité de la condition, même lorsqu'elle est au milieu, contrairement à la sortie anticipée qui nécessite d'inverser la polarité (en ajoutant ` not` ), fonctionnant comme `until` au lieu de `while` .
Cette structure n'est pas largement prise en charge, la plupart des langages utilisant plutôt if ... break pour une sortie anticipée conditionnelle.
Ceci est pris en charge par certains langages, tels que Forth , où la syntaxe est BEGIN ... WHILE ... REPEAT, et les langages de script shell Bourne shell ( sh) et bash , où la syntaxe est while ... do ... done ou until ... do ... done , comme :
Concrètement, une variante de boucle est une expression entière dont la valeur initiale est non négative. La valeur de cette variante doit diminuer à chaque itération de la boucle, mais ne doit jamais devenir négative pendant son exécution. Les variantes de boucle permettent de garantir la terminaison des boucles.
Un invariant de boucle est une assertion qui doit être vraie avant la première itération et rester vraie après chaque itération. Cela signifie que lorsqu'une boucle se termine correctement, la condition de sortie et l'invariant de boucle sont tous deux satisfaits. Les invariants de boucle servent à contrôler certaines propriétés d'une boucle au cours des itérations successives.
Certains langages de programmation, comme Eiffel, intègrent une prise en charge native des variantes et des invariants de boucles. Dans d'autres cas, cette prise en charge est un ajout, comme la spécification du langage de modélisation Java pour les instructions de boucle en Java .
Sous-langage de boucle
Certains dialectes Lisp proposent un sous-langage étendu pour la description des boucles. On en trouve un exemple ancien dans Conversional Lisp d' Interlisp . Common Lisp fournit une macro Loop qui implémente un tel sous-langage.
Tableau de correspondance des systèmes de boucles
| langage de programmation | Conditionnel | Boucle | Sortie anticipée | continuation de la boucle | Refaire | Réessayer | Installations de correction | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Commencer | Milieu | Fin | Numérique | Collection | Général | Infini [1] | Variante | Invariant | |||||||||||||||||||||||||||||||
| Ada | APL | [3] | C | [2] | [3] | [3] | C++ | [2] | [9] | [3] | [3] | C# | [2] | [3] | [3] | COBOL | [15] | [14] | Common Lisp | [16] | D | [14] | Eiffel | [10] | [10] | [11] | [13] | Fa# | [6] | FORTRAN 77 | Fortran 90 | Fortran 95 et versions ultérieures | Aller | Haskell | [6] | Java | [2] | [12] | [12] |
| JavaScript | [2] | Kotlin | OCaml | [6] | [17] | [5] | [17] | PHP | [2] [5] | [4] | Perl | [2] [5] | Python | [5] | [6] | [6] | QB64 | Rebol | [7] | [8] | [6] | Rubis | [6] | [6] | Rouiller | [5] | ML standard | [6] | Rapide | Visual Basic .NET | PowerShell | [2] | Zig | La boucle « a » n'est pas considérée comme une boucle infinie à cet effet, car il ne s'agit pas d'une structure de langage dédiée.while (true)for (init; test; increment)range()whilefonction peut être utilisée à cette fin.std::for_eachmodèlequi permet d'itérer surles conteneurset d'appeler unefonction unairepour chaque élément. Cette fonctionnalité peut également être implémentée sous formede macrosur ces conteneurs.retry, mais celui-ci est utilisé dansla gestion des exceptionset non dans le contrôle des boucles.GO TOet en procédant.forNon localDe nombreux langages de programmation, notamment ceux qui privilégient les styles de programmation dynamiques, offrent des mécanismes de contrôle de flux non local permettant de passer de l'exécution courante à un point prédéfini . Quelques exemples notables suivent. Gestion des conditionsLes premiers compilateurs Fortran prenaient en charge des instructions pour gérer les conditions exceptionnelles, notamment `true` PL/I possède environ 22 conditions standard (par exemple, ZERODIVIDE SUBSCRIPTRANGE ENDFILE) qui peuvent être déclenchées et interceptées par : l'action de condition ON ; Les programmeurs peuvent également définir et utiliser leurs propres conditions nommées. Comme pour l'instruction if non structurée , une seule instruction peut être spécifiée ; dans de nombreux cas, une instruction GOTO est donc nécessaire pour déterminer où le flux de contrôle doit reprendre. Malheureusement, certaines implémentations présentaient une surcharge importante en termes d'espace et de temps (notamment SUBSCRIPTRANGE), si bien que de nombreux programmeurs ont essayé d'éviter d'utiliser des conditions. Un exemple typique de syntaxe : Sous condition, allez à l' étiquette Gestion des exceptionsDe nombreux langages modernes prennent en charge nativement la gestion des exceptions . Généralement, le flux de contrôle des exceptions commence par la levée d'une exception. Le contrôle est alors transmis au gestionnaire d'exceptions le plus interne de la pile d'appels . Si ce gestionnaire traite l'exception, le flux de contrôle reprend son cours normal. Sinon, le contrôle se propage vers les gestionnaires externes jusqu'à ce que l'un d'eux traite l'exception ou que le programme atteigne la portée la plus externe et se termine. À mesure que le contrôle se propage vers les gestionnaires externes, les opérations qui se produiraient normalement, telles que le dépilement de la pile d'appels, sont gérées automatiquement. Le code C++ suivant illustre la gestion structurée des exceptions. Si une exception est propagée lors de l'exécution De nombreux langages utilisent les mots-clés C++ `catch` Dans de nombreux langages (dont Object Pascal, D, Java, C# et Python), une Ce schéma étant fréquent, C# fournit l' Continuationinformatique , une continuation est une représentation abstraite de l'état de contrôle d'un programme . Elle concrétise cet état, c'est-à-dire qu'il s'agit d'une structure de données représentant le processus de calcul à un instant donné de son exécution. Cette structure est accessible par le langage de programmation, contrairement aux structures de données masquées dans l' environnement d'exécution . Les continuations sont utiles pour implémenter d'autres mécanismes de contrôle, tels que les exceptions , les générateurs , les coroutines , etc. La « continuation courante » ou « suite de l'étape de calcul » est la suite qui, du point de vue de l'exécution du programme, serait obtenue à partir du point actuel de son exécution. Le terme « continuations » peut également désigner les continuations de première classe, c'est-à-dire les constructions qui permettent à un langage de programmation de sauvegarder l'état d'exécution à tout moment et d'y revenir ultérieurement, éventuellement à plusieurs reprises. Générateurinformatique , un générateur est une routine permettant de contrôler le comportement d'itération d'une boucle . Tous les générateurs sont également des itérateurs . Un générateur est très similaire à une fonction renvoyant un tableau : il possède des paramètres , peut être appelé et génère une séquence de valeurs. Cependant, au lieu de construire un tableau contenant toutes les valeurs et de le renvoyer simultanément, un générateur produit les valeurs une à une, ce qui nécessite moins de mémoire et permet à l'appelant de commencer immédiatement le traitement des premières valeurs. En résumé, un générateur ressemble à une fonction mais se comporte comme un itérateur . Les générateurs peuvent être implémentés en termes de constructions de flux de contrôle plus expressives, telles que les coroutines ou les continuations de première classe . Les générateurs, également connus sous le nom de semi-coroutines, sont un cas particulier (et plus faible que) les coroutines, en ce sens qu'ils rendent toujours le contrôle à l'appelant (lorsqu'ils renvoient une valeur), plutôt que de spécifier une coroutine vers laquelle sauter ; voir la comparaison des coroutines avec les générateurs . CoroutineLes coroutines sont des composants de programme informatique pouvant être suspendus et repris — une généralisation des sous-programmes — pour le multitâche coopératif . Elles sont particulièrement adaptées à la mise en œuvre de composants de programme courants tels que les tâches coopératives , les exceptions , les boucles d'événements , les itérateurs , les listes infinies et les tubes . Elles ont été décrites comme des « fonctions dont vous pouvez suspendre l’exécution ». Melvin Conway a inventé le terme coroutine en 1958 lorsqu'il l'a appliqué à la construction d'un programme d'assemblage . La première explication publiée de la coroutine est apparue plus tard, en 1963. COMEFROMprogrammation , l'instruction COMEFROM est une instruction de contrôle de flux qui provoque un saut à l'instruction suivante lorsque le contrôle atteint le point spécifié par son argument COMEFROM. Cette instruction est l'inverse de l'instruction goto et relève davantage de la plaisanterie que de la science informatique sérieuse . Le point de saut spécifié est souvent identifié par une étiquette . Par exemple, COMEFROM xl'instruction COMEFROM indique que lorsque le contrôle atteint l'étiquette x, il reprend son exécution à l'instruction suivant le COMEFROM.Une différence majeure avec l'instruction `goto` réside dans le fait que `goto` dépend de la structure locale du code, tandis que `COMEFROM` dépend de la structure globale. Une instruction `goto` transfère le contrôle lorsque celui-ci l'atteint, tandis que `COMEFROM` exige que le processeur (c'est-à-dire l'interpréteur) recherche les instructions `COMEFROM` afin que, lorsque le contrôle atteint l'un des points spécifiés, le processeur puisse effectuer le saut. La logique qui en résulte est souvent difficile à comprendre, car aucun indice n'indique à proximité d'un point de saut que le contrôle va effectivement sauter. Il est nécessaire d'étudier l'intégralité du programme pour vérifier si des instructions `COMEFROM` font référence à ce point. La sémantique d'une instruction COMEFROM varie selon le langage de programmation . Dans certains langages, le saut a lieu avant l'exécution de l'instruction au point spécifié, et dans d'autres, après. Selon le langage, plusieurs instructions COMEFROM référençant le même point peuvent être invalides, non déterministes, exécutées dans un ordre particulier, ou induire un traitement parallèle ou concurrent, comme illustré dans Threaded Intercal . L'instruction COMEFROM est apparue initialement dans des listes d'instructions humoristiques en langage assembleur (sous la forme « CMFRM »). Elle a été développée dans un article de Datamation par R. Lawrence Clark en 1973 , en réponse à la lettre d' Edsger Dijkstra intitulée « Go To Statement Considered Harmful ». COMEFROM a finalement été implémentée dans la variante C-INTERCAL du langage de programmation ésotérique INTERCAL, ainsi que dans l'instruction encore plus obscure « computed COMEFROM ». Des propositions ont également été faites en Fortran pour « assigned COME FROM » et une instruction « DONT » (pour compléter la boucle « DO » existante). Sortie anticipée de boucle imbriquée basée sur un événementLe modèle de Zahn a été proposé en 1974, et discuté dans la gestion des exceptions , et les exceptions ou des constructions similaires sont utilisées à cette fin dans de nombreux langages. L'exemple simple suivant consiste à rechercher un élément particulier dans un tableau bidimensionnel. sortir lorsqu'il est trouvé ou manquant ; pour I := 1 à N faire pour J := 1 à M faire si table[I,J] = cible alors trouvé ; manquant; sorties trouvé : imprimer ("l'élément est dans le tableau"); manquant : impression ("l'élément n'est pas dans le tableau") ; fin de sortie ; | |||||