goto — Les-mathematiques.net The most powerful custom community solution in the world

goto

Ce post fait suite a la discussion amorcee dans le fil C++ et grand nombre (ferme car hors sujet).
Je pense que si les concepteurs du langage C/C++ ont conserve le mot-clef goto, c'est qu'il est utile dans certaines situations. Par exemple dans une situation ou il y a plusieurs tests a faire sur des variables de type non entiers, on ne peut pas utiliser une structure switch/case/break et il faut alors utiliser des if/else imbriques ce qui rend le code peu lisible. Une solution pour rendre le code lisible sans goto consiste a utiliser une fonction pour cette partie de code avec des return ce qui evite d'introduire des blocs else imbriques, mais c'est parfois complique et couteux a mettre en oeuvre (par exemple s'il y a plusieurs variables impliquees dans les tests ou si on modifie plusieurs variables dans les blocs), dans ces conditions un goto est plus lisible et efficace.
Adopter une position ideologique anti-goto simplement parce que le code etait difficile a suivre avant la generalisation des langages permettant la programmation structuree est a mon avis une absurdite. Il ne faut pas non plus oublier qu'en assembleur, on ne dispose pas de structure de boucle mais de branchements inconditionnels et conditionnels, s'interdire de les utiliser en programmation structuree (surtout dans un langage comme le C) c'est aussi ridicule que de s'interdire d'utiliser des boucles dans un langage a priori fonctionnel et recursif (ce qui etait le cas dans certaines implementations de logo si je ne m'abuse).
Sinon, pour repondre a Fin de partie, c'est justement parce que je travaille sur des projets faisant des centaines de milliers de lignes de code (dont 200 000 lignes de code dont je suis l'auteur pour giac/xcas), que je donne cet avis.
«13

Réponses

  • Bonjour,

    Théorème:
    Tout programme écrit dans un langage raisonnable et utilisant des goto peut être ré-écrit sans goto.

    Ceci dit, Il m'est arrivé de programmer dans des langages exotiques en utilisant des goto, parce que je n'avais pas le choix, par exemple le macro-langage de GMP2D (années 80), mais en faisant un effort maximum de structuration.
    Par contre, en C, C++, C#, il n'y a pas de raison.
    Et j'ai aussi écrit des programmes avec beaucoup de lignes.

    Cordialement,

    Rescassol
  • Une question : comment, sans goto, écrire un programme où deux boucles ou deux tests se croisent ?
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Rescassol, bien entendu on peut ecrire un programme sans goto. Cela ne signifie pas pour autant qu'il sera plus lisible et facile a maintenir que la version avec goto. Le tout est d'utiliser une structure ou un goto a bon escient. J'ai le sentiment que c'est l'avis de Knuth http://cs.sjsu.edu/~mak/CS185C/KnuthStructuredProgrammingGoTo.pdf
    et Linus Torvalds
    http://web.archive.org/web/20051128093253/http://kerneltrap.org/node/553/2131
  • Par exemple dans une situation ou il y a plusieurs tests a faire sur des variables de type non entiers, on ne peut pas utiliser une structure switch/case/break et il faut alors utiliser des if/else imbriques ce qui rend le code peu lisible
    En C++ on peut aussi faire des "else if" (l'équivalent du elif de d'autres langages). C'est parfaitement lisible, et on n'augmente pas l'imbrication des blocs.

    Exemple (tiré de ce tutorial http://www.cplusplus.com/files/tutorial.pdf ) :
    if (x > 0)
      cout << "x is positive";
    else if (x < 0)
      cout << "x is negative";
    else
      cout << "x is 0";
    

    A l'instar de nombreux langages, il y a en C++ une simplification de syntaxe dans le cas du else if.
  • Une question : comment, sans goto, écrire un programme où deux boucles ou deux tests se croisent ?
    Le "théorème" de Rescarol implique que tout programme avec deux boucles qui se croisent est équivalent à un programme où aucune boucle ne se croisent.

    Pour le démontrer, on peut simplement transformer le programme avec Gotos comme suit :
    • Faire du programme initial une liste d'instruction
    • ajouter une variable : le pointeur d'instruction
    • Faire un seul gros while
    • A l'intérieur du While, on fait un énorme switch : un cas par instruction.

    On a même montré plus fort : une seule boucle while suffit.

    Après, effectivement, c'est très moche comme programme.
  • Je suis d'accord avec Parisse: goto nous manque parfois quand on n'a pas envie de réfléchir 15mn à comment le contourner. Bien sûr "théoriquement", il est contournable, mais franchement, ici on est "en pratique": on va pas faire comme PR le décrit, "simuler" tous le programme à chaque fois qu'on aurait besoin d'un goto.

    Les anti-goto devraient se fendre d'un argument montrant qu'au prix max d'une ou deux lignes on peut le contourner, pas juste d'une réponse théorique. Encore une fois, on trouve ici des manifestations de la religion protypage, en gros une idéologie qui voudrait presque "interdire" aux programmeurs de ne pas bien typer leurs programmes. Et ça se traduit dans les faits.
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • Un exemple concret bidon en pseudo basic :
    10 initialisation
    20 ligne bidon
    30 ligne bidon
    40 si test vérifié alors aller en 20
    50 autre ligne bidon
    60 si test vérifié alors aller en 30
    70 sortie
    
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Ou encore:

    procedure debile (a: entier; var s:entier)
    var b : entier;
    begin
    1) b:= random entier entre 2 et 6;
    2) incrementer s
    3) a:=a+s
    4) debile(s,a); debile(a+b,s)
    5) goto b
    6) if a est pair then begin s:=10s; goto (random entier 1 et 4) end;
    end;
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • Je me demande si on ne mêlange pas "algorithmique" et "programation".
    Le principe général d'un programme informatique est la suite des instructions.
    Si on cherche à écrire un ensemble de fonctions de calcul élémentaires, les lignes de code seront du type linéaire. Dans la pratique, l'informatique offre des possibilités beaucoup plus grandes.
    Un simple boucle (for, while, do) contient obigatoirement une instruction du type jump. Les auteurs de langages ont masqué cela pour les boucles.
    Pour les sorties de boucles, il y a la commande break, qui veut simplement dire "goto la ligne suivante de la fin de boucle".
    Par curiosité, j'ai regardé dans mon programme si j'utilisais beaucoup de goto. Y'en a pas mal, mais ce sont des trucs du genre goto FIN, goto SUITE, goto Err etc.
    Le langage C permet ce type de saut, le "théorème" cité par Rescassol est indéfendable, de même que l'argument "facilité".
    Si on code avec ce type d'idées préconçues, on ne codera pas bien longtemps.
  • Professeur rectangle, personnellement j'ai du mal avec cette ecriture, je suis oblige de rajouter des {} pour bien me convaincre de ce qui va se passer. De toutes facons, si on a plusieurs instructions a effectuer, il faudra bien les mettre dans un bloc. Apres, je n'ai peut-etre pas choisi un tres bon exemple (il vaut mieux regarder le post de Linus pour ca) car je n'utilise pas l'instruction goto elle-meme (peut-etre parce que j'ai subi le brainwashing des anti-goto). Par contre je suis un tres gros utilisateur des goto "implicites" que sont break, continue ou return dans une boucle, et pour ceux-la je n'ai aucun doute de leur superiorite sur les ersatz a coup de booleen artificiel que les anti-break utilisent.
  • Bonjour,

    @Professeur Rectangle, je suis Rescassol et non Rescarol.

    @CC tu n'as pas à sortir ton artillerie loggorhéo-idéologique.

    Il se trouve que j'ai rencontré beaucoup d'étudiants écrivant des programmes en Basic ou Fortran, et qui, au fur et à mesure d'évolutions et de debugging, transformaient ça en sacs de noeuds illisibles à force d'abus de gotos.
    Ça ne veut pas dire qu'on ne peut pas écrire un goto proprement, mais je maintiens qu'on peut s'en passer de façon tout aussi propre.
    Si je n'aime pas les gotos, c'est simplement parce que les autoriser ne va pas dans le sens d'une bonne pédagogie de programmation.

    Cordialement,

    Rescassol
  • parisse a écrit:
    Par contre je suis un tres gros utilisateur des goto "implicites" que sont break

    Et tu as raison et de plus, tu ne risques pas d'être contredit par des théorèmes à la Rescassol ou PR, car sans break (ou instruction de contrôle de nature similaire), l'informatique ne réalise que la logique intuitionniste (autrement dit, essentiellement, pour être complèt un compilateur doit obligatoirement implémenter un break, et c'est un théorème fondamental, pas un théorème de maths appli (essentiellement dû à Timothy Griffin).

    Je prends l'exemple académique du caml (c'est un "langage idéal" théoriquement). Il type automatiquement tous les programmes (quitte à créer des types paradoxaux) et est complet (tout ce qui est programmable, l'est en caml). Par ailleurs, si on ôte la commodité "rec" de "let rec", on obtient "ce qui est réalisable" (cela donne un bon critère pour juger de la valeur d'un langage). Et bien si EN PLUS on ôte "raise" (c'est l'instruction "break" de caml) , je mets au défi quiconque de réussir (sans rec, ni "raise", ni artifice utilisant des listes vides, etc) à obtenir:

    blabla ;;
    val callcc : (('a -> 'b) -> 'a) -> 'a = <fun>
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • @Rescassol: je ne voulais pas être désagréable, mais toi tu l'es franchement :-D Bien entendu, tu peux bien penser que c'est mieux, je précisais juste quelques points (et pas d'idéologie dedans). Par contre, je le maintiens il ya de l'idéologie dans le fait de vouloir tout typer (mais je ne dis pas que TOI, t uveux tout typer).
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • Rescassol a écrit:
    Il se trouve que j'ai rencontré beaucoup d'étudiants écrivant des programmes en Basic ou Fortran, et qui, au fur et à mesure d'évolutions et de debugging, transformaient ça en sacs de noeuds illisibles à force d'abus de gotos.

    Je suis étonné par ce témoignage. J'ai écrit mon dernier goto en 1988, quand j'ai abandonné mon MO5 et son Basic pour un PC et Turbo Pascal.
    A l'époque, les ordinateurs personnels étaient livrés avec Basic en Rom. Mais aujourd'hui, qui rencontre encore Basic ?
  • Bonjour,

    Aléa, mon expérience étudiante en informatique remonte à 1980 et les étudiants privilégiés possédaient un Apple II avec Basic AppleSoft, j'ai ensuite rencontré Fortran à l'IUT de Cachan en 1982.

    Cordialement,

    Rescassol

  • Vers cette époque, quand j'étais au lycée on allait quelque fois s'initier à l'IUT de Cachan.... au Basic sur des ordinateurs Sun microsystème si je me souviens bien.
    Si tu tolères les GOTO dans du code, pour coder plus vite, c'est à dire sans réfléchir, cela va devenir vite illisible et non maintenable. Quand tu codes en assembleur le point de vue est différent. B-)-
  • @L'Ersatz

    Pourrait-on avoir une petite exégèse de ton article (ton lien)? Il est assez long, en anglais et a l'air intéressant. Mais je n'ai pas la dispo.
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • Fin de Partie,
    Peux-tu expliquer la différence qu'il y a en C entre un break, un continue, un return, un switch et un goto ?
    Tout ça, c'est exactement la même logique, la seule différence est qu'il ne sont pas interchangeables, d'où la nécessité de chacun, y compris le goto.
  • Bonjour,

    En1982, à l'IUT de Cachan, et j'y suis resté jusqu'en 1988, j'étais en DEA d'informatique à Paris 6 (et prof de lycée à Drancy), et j'y étais vacataire dans la formation continue de CAO dirigée par Michel Carrard.
    J'y ai fait du Fortran, du C, du Pascal et des méthodes de lissage, plus de l'encadrement de projet.
    Au début, on avait un PDP 11/34 avec 64 Ko de Ram en mutipostes sur une douzaine de postes.

    Cordialement,

    Rescassol
  • "N'utilisez pas de goto" est un commandement simple que l'on donne (ou donnait) aux étudiants pour les forcer à structurer leurs programmes.

    Quelqu'un qui maîtrise la programmation peut très bien se passer de ce commandement simpliste. Utiliser des goto qui vous envoient à l'autre bout du code est sans doute toujours une mauvaise idée. Utilisez un goto (ou un avatar) pour aller dix lignes plus loin ou plus haut dans un petit bout de code n'est a priori pas criticable.
  • Je redis en termes plus simples ce que j'ai dit à mon précédent post. Le "goto" ou ses avatars sont plus que défendables, ils sont indispensables en l'état actuel du paradigme informatique. Les théorèmes évoqués par Rescassol ou PR ont des conditions de validité beaucoup trop limitées: ils concernant les bons vieux programmes déterministes qui prennent un truc simple en entrée et renvoient un résultat en sortie.

    99% des logiciels ne servent pas à ça:

    1) Ils bouclent par vocation (un OS qui ne bouclerait pas serait un scandale)
    2) Ils traitent des données non déterministes
    3) Ils peuvent devoir être appelés à être compatibles avec la programmation parallèle asynchrone.

    Le typage ressemble à la logique (écrite avec le signe "implique"). On peut prouver que la programmation structurée (sans goto ni avatar) réalise essentiellement la logique intuitionniste (et ses prolongements). Cela démontre aussi que "goto" (ou autre avatar de même nature) sont nécessaires. En effet, la logique classique est (et se doit être) réalisable. Qu'est-ce que c'est la logique classique (ici)? C'est la possibilité, quand on vous donne une garantie de (A=>B)=>A de malaxer le produit si j'ose dire pour le magnifier en une garantie de A.

    Tant que les A,B,C sont des phrases et que la flèche veut dire "implique", ça ne pose pas de problème: les matheux, même non logiciens (exemple SN récemment) sortent leur révolver (ie leur table de vérité) et disent "bon, bon, bon: si A est faux, alors, A=>B, donc A. Si A est vrai alors A"

    Mais ça ne marche pas comme ça en informatique: les lettres majuscules ci-dessus ne fonctionnent pas forcément comme des phrases "classiques". La plupart des garanties de la vie courantes sont difficilement assimilables à des phrases classiques. il n'y a pas de "vrai" et de "faux" chers aux platoniciens. On dispose bien de =>, et de non (précision: non(A) est le type des noms qu'on donne aux objets vivant dans A, ie aux pointeurs vers A), mais pas de "étant donné 3 phrases, deux d'entre elles sont équivalentes".

    Donc question: comment réaliser l'objectif A quand on nous donne en entrée un objet de type f: (A=>B)=>A. Je reraconte pour la 236878ième fois, comment l'informaticien fait: il propose à f un agent inflitré que j'appelle u qui a l'effet suivant. Si lors de l'exécution de f(u), la procédure implémentant f demande à u(truc) de s'exécuter (où truc est de type A), et bin, u va trahir: il va tout stopper, mettre truc dans sa poche, et l'emmener à l'appelant émetteur. Par contre, si f ne se sert pas de u, on laisse couler.

    Ca donne la programmation suivante:
    function a: A begin réserver l'adresse 123; exécute f(u) // si exception se produit récupère la donnée à l'adresse 123 end

    au préalable: on a programmé u comme suit: function u(x:A): B begin enregistre x à l'adresse 123; déclencher exception Stop; end

    (Pour ceux qui aiment le caml:

    exception Stop
    let bluf r x = begin r:=x:: (!r); raise(Stop) end

    let callcc u = let r=ref [] in try u (bluf r)
    with | Stop ->
    match (!r) with
    | []->raise Stop
    | x::li->x;;


    donnant comme réponse du compilateur:

    exception Stop
    val bluf : 'a list ref -> 'a -> 'b = <fun>
    val callcc : (('a -> 'b) -> 'a) -> 'a = <fun>

    )

    Exercice: montrer que sans procédure "exit" ou goto ou n'importe quel avatar, il existe des programmes faisant des choses avec (dans le paradigme élargi) qui ne sont simulables par aucun programme qui n'en disposent pas.
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • @NP :
    10 initialisation
    20 ligne bidon
    30 ligne bidon
    40 si test vérifié alors aller en 20
    50 autre ligne bidon
    60 si test vérifié alors aller en 30
    70 sortie
    

    Un essai sans goto :

    Soit A la procédure
    test1:=vrai;
    tant que test1
     {ligne 20
      ligne 30
      test1:=(...)
     }
    

    Soit B la procédure
    test2:=vrai;
    tant que test 2
     {ligne 30
      A
     ligne 50
     test2:=(...)
     }
    

    Finalement, le programme devient
    ligne 10
    A
    B
    sortie
    
  • C’est quand même plus élégant avec goto qui éviter la duplication de code.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Codeur:

    Je ne comprends pas ce que vient faire ici le goto.

    Un break permet de sortir d'une boucle, un continue permet, sauf erreur, de zapper tout ce qui suit dans la boucle et de reprendre la boucle au départ mais dans la continuité. Un return permet de sortir d'une fonction, ou du programme s'il est exécuté hors du code d'une fonction/sous-programme, un switch est une généralisation au if else qui permet de structurer le code.
    Le goto marque une rupture inconditionnelle dans l'exécution du code , on peut mettre le label où on veut, dans une boucle pour em... celui ou celle qui va avoir à faire la maintenance du code. B-)
  • Le break provoque la suite du programme à la ligne qui suit la fin du bloc.
    Le continue provoque la suite du programme à dernière ligne du bloc.
    Le return provoque la suite du programme à la dernière ligne de la fonction.
    Le switch est une liste de goto numérotés.
    Le goto provoque la suite du programme à une adresse spécifiée.

    Un compilateur normal n'accepte pas l'adresse de destination d'un goto à l'intérieur d'aun bloc.
    En fait, on ne peut utiliser un goto qu'avec un rigeur particulière.
    Par exemple ce code
    for (...)
    {
    ADRESSE :
    {
    ...
    }
    }
    goto ADRESSE;

    ne passera pas.

    Le goto est une instruction comme une autre qui doit être utilisée lorsque c'est plus pratique et plus clair.
    Si tu arrives à compiler un code avec un label de goto n'importe où, merci de bien vouloir le copier et indiquer le nom du compilateur.
  • Ben avec un compilateur de code en assembleur, c'est pas un souci.
    Tu peux même faire un saut au milieu d'une instruction (vieille astuce pour planter les débugueurs).
  • Oui, en assembleur on fait tout ce qu'on veut, mais là il s'agit de programmation en C ou C++.
  • Ben on peut inclure un .obj ou faire de l'asm inline avec un bon compilateur C++ ! ;-)
  • parisse a écrit:
    Par contre je suis un tres gros utilisateur des goto "implicites" que sont break
    Et tu as raison et de plus, tu ne risques pas d'être contredit par des théorèmes à la Rescassol ou PR, car sans break (ou instruction de contrôle de nature similaire), l'informatique ne réalise que la logique intuitionniste (autrement dit, essentiellement, pour être complèt un compilateur doit obligatoirement implémenter un break, et c'est un théorème fondamental, pas un théorème de maths appli (essentiellement dû à Timothy Griffin).

    Je prends l'exemple académique du caml (c'est un "langage idéal" théoriquement). Il type automatiquement tous les programmes (quitte à créer des types paradoxaux) et est complet (tout ce qui est programmable, l'est en caml). Par ailleurs, si on ôte la commodité "rec" de "let rec", on obtient "ce qui est réalisable" (cela donne un bon critère pour juger de la valeur d'un langage). Et bien si EN PLUS on ôte "raise" (c'est l'instruction "break" de caml) , je mets au défi quiconque de réussir (sans rec, ni "raise", ni artifice utilisant des listes vides, etc) à obtenir:

    blabla ;;
    val callcc : (('a -> 'b) -> 'a) -> 'a = <fun>
    Ce n'est pas parce que certains types ne sont pas possibles que le langage n'est pas complet.
    Typiquement, le type que vous proposez n'est pas possible en C à cause du polymorphisme. Diriez-vous que C n'est pas complet ?

    Bref, ne pas confondre complétude et typage.


    Remarque : Pourquoi choisir un type si compliqué et pas tout simplement 'a->'b ? Sauf erreur de ma part, on a besoin de la récursivité ou des exceptions pour faire 'a->'b.
  • @PR: attention aux confusions. Les langages C, PAscal, assembleur, etc, sont impératifs et la notion de "type" n'a pas grand sens: plus précisément, elle a plusieurs sens, l'un étant étant donné par les auteurs du compilateur lui-même et n'ayant presque rien à voir (en exagérant) avec la notion correcte de type), l'autre étant une notion "externe" qu'on définit formellement "à côté" du langage, si on peut, pour l'évaluer. Bien évidemment, une analyse routinière du C, du Pascal ou de l'assembleur montre qu'il sont complets au niveau des types qu'il réalisent, ie il réalisent tout ce qui est possible informatiquement, à savoir (si on se restreint aux formes propositionnelles vierges) la logique classique. Ils disposent tout comme le caml ou n'importe quel langage sérieux à vocation généraliste d'instruction primitives permettant d'implémenter "callCC" même si c'est moins propre (en Pascal, c'est "try...finally" en C, je ne me rappelle pas, je ne fais jamais de C, et en assembleur, les différents jmp). Tu sembles avoir posté en pensant au typage d'auteur, ie les très rares types annoncés officiellement par les auteur des compilateurs. Là :-D c'est sûr qu'il n'y en a pas beaucoup!!
    PR a écrit:
    Pourquoi choisir un type si compliqué et pas tout simplement 'a->'b ? Sauf erreur de ma part, on a besoin de la récursivité ou des exceptions pour faire 'a->'b.

    Enfin une question!! Une fois de plus dans ton premier paragraphe, tu as adopté un style docte qui cache que tu voulais poser une question. Sache (je me répète et il n'y a pas d'animosité) que c'est ambigu pour les visiteurs google qui cherchent des infos. Après tout, on poste "aussi" pour eux, non?

    Mon but était de convaincre de quelque chose. La caml implémente directement (ie sans demander à l'auteur des programmes) une inférence de typage: ce qui signifie que quand tu tapes un programme, à la fin, sans que tu ne fasse rien, il te renvoie "val" suivi de ce qui s'apparente à un "énoncé" (qui ressemble un peu à du langage mathématique écrit avec "=>") qui doit être lu comme "prouvé" par le programme que tu viens de taper (sinon, il t'envoie dans les choux en te disant qu'il ne peut pas typer ton programme). Tout ceci est inspiré de la correspondance de Curry Howard, et même, très précisément, implémente en "chair et en os" si j'ose dire le lambda-calcul. Bien évidemment, l'INRIA ne pouvait pas résoudre tous les problèmes (dont certains sont ouverts encore actuellement !!!) afférant à cette spécialité. Contrairement à COQ par exemple, et d'autres outils, je trouve qu'il ont fait le meilleur choix (celui de ne pas brider le programmeur X ou Y, pas forcément féru de logique informatique, et éventuellement appelé à programmer en caml des projets réellement industriels et important): prendre par la main le programmeur en douceur, en lui "imposant" les découvertes de la CorCuHO, tout en lui permettant de programmer ce qu'il veut (ie de ne pas ôter de puissance à ce que l'informatique permet théoriquement de faire). Dès lors, il était inévitable (puisque c'est un problème non résolu même en maths) d'accepter de considérer des fonctions plutôt que des applications.
    Autrement dit, une fonction partielle (par exemple l'ensemble vide), allant de A dans B était acceptable et devait pouvoir recevoir le type $A\to B$.

    Par contre, quand on évalue ce que réalise une machine, il faut être honnête. Je ne peux pas poster sur le forum, et utiliser un argument qui dit que le caml est puissant car il réalise le type $\forall X, Y: (X\to Y)$ en exhibant la fonction vide qui l'habite. La fonction vide habite tous les $A\to B$ quelque soit $A,B$. Je ne vais détailler ce qu'on appelle "être honnête" mais ça reçoit une définition formelle. Une fois acceptée cette définition, il y a un point important:

    Toute machine ne peut réaliser au mieux, parmi les énoncés de la forme $\forall X_1,...,X_n: blabla$, où les lettres sont inanalysées, que les théorèmes de maths, ie que les tautologies du calcul propositionnel (auxquel on a rajouté les $\forall$ devant, sauf à utiliser "rec" (Exercice: le démontrer, ce n'est pas très dur)


    Il y a plusieurs définitions de ce que veut dire "être honnête" qui sont toutes plus ou moins équivalentes. Les concepteurs de caml ont pris le plus grand soin qu'il était possible de prendre pour éviter les raccourcis invalides. Par exemple, si tu tapes:

    raise;;
    - : exn -> 'a = <fun>


    le mot "raise" est une instruction primitive du caml. Elle est "de toute pièce" une instruction qui semble garantir l'accès au paradis (puisque 'b peut être n'importe quoi à partir du moment où on lui fournit un objet de type "exception"

    Autre exemple, si tu tapes:

    # let r = ref [];;
    val r : '_a list ref = {contents = []}
    # let f x = match (!r) with
    | y::li ->y;;

    Characters 10-41:
    ..........match (!r) with
    | y::li ->y..
    Warning 8: this pattern-matching is not exhaustive.
    Here is an example of a value that is not matched:
    []
    val f : 'a -> '_b = <fun>


    Le compilateur accepte de reconnaitre que tu as "réussi" à trouver un élément de $A\to B$ pour $A,B$ quelconques, mais il signale quand-même un petit underscore et un warning pour dire que ta fonction est très partielle.

    Pour enlever l'underscore, tu rends locale la liste vide:

    let f x = let s = ref [] in match (!s) with y::li -> y;;
    Characters 28-54:
    let f x = let s = ref [] in match (!s) with y::li -> y;;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
    Warning 8: this pattern-matching is not exhaustive.
    Here is an example of a value that is not matched:
    []
    val f : 'a -> 'b = <fun>


    Etc, etc. De toute façon, toutes ces programmations sont une manière de tricher (ie une triche explicitement autorisée par l'INRIA pour ne pas brider caml) en exhibant l'ensemble vide comme une fonction partielle habitant $X\to Y$. Attention: il n'y a absolument pas besoin du type exn pour le faire (tu posais la question). Par contre, il est inévitalbe que je reçoive des "warning" ou alors que je les court-circuite en exploitant exn.

    Bref, une fois éliminé tous ces "à côtés", on parvient (et c'est usuel) à dresser la liste de ce qu'un langage permet "VRAIMENT" de réaliser (comme sous-ensemble de l'ensemble de tautologies), et quand il est réalise toutes les tautologies , il est théoriquement de puissance maximale (ie il suffit de lui rajouter rec pour avoir l'équivalent de l'assembleur en puissance). C'est pourquoi, j'ai expliqué comment réaliser (sans tricher) le seul axiome que la logique intuitionniste ne contient pas et qui, quand on le lui ajoute, entraîne toute la logique classique, à savoir $((A\to B)\to A)\to A$

    Une "bonne" analyse des langages impératifs serait assez compliquée, d'autant qu'il faudrait donner une vraie importance à l'opération $\circ$ qui est le commuté du "point-virgule".

    Edit: j'ai mis en bleu ce que j'ai tapé et en vert ce que le compilateur répond.
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • Précision (je n'avais pas donné la correction):

    # exception Stop
    let bluf r x = begin r:=x:: (!r); raise(Stop) end
    let callcc u = let r=ref [] in try u (bluf r)
    with | Stop ->
    match (!r) with
    | []->raise Stop
    | x::li->x
    ;;

    exception Stop
    val bluf : 'a list ref -> 'a -> 'b = <fun>
    val callcc : (('a -> 'b) -> 'a) -> 'a = <fun>


    edit: oups, si, bon pas grave

    Bon bin, j'améliore pour la précision de ma réponse à PR

    Dans :

    exception Stop
    exception GrosBug

    let bluf r x = begin r:=x:: (!r); raise(Stop) end

    let callcc u = let r=ref [] in try u (bluf r)
    with | Stop ->
    match (!r) with
    | []->raise GrosBug
    | x::li->x;;

    exception Stop
    exception GrosBug
    val bluf : 'a list ref -> 'a -> 'b = <fun>
    val callcc : (('a -> 'b) -> 'a) -> 'a = <fun>



    L'exploitation de ce procédé ne déclenchera JAMAIS l'exception GrosBug.
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • Je lis en diagonale les 10 regles en puissance, et je me demande bien comment on peut ecrire un logiciel de calcul formel avec ca... pas de recursion, pas d'allocation dynamique de memoire,...
  • All loops must have a fixed upper-bound

    C'est une blague. Evidemment tous ces textes normatifs, écrits en anglais, sont écrits par des despotes qui ne voient que leur petit monde (la comptabilité du resto routier qui borde l'autoroute qu'ils prennent le matin pour aller dans leur boite)

    Il faut reconnaître que dans le privé 99% des programmeurs ne font que ça (des logiciels triviaux accomplissant des tâches pratiques triviales), et se moquent éperdument de ce que permettrait "théoriquement" de faire un ordinateur. Il y a deux mondes professionnels en fait: les uns qui essaient d'améliorer des logiciels triviaux comme un "word" qui marche plus vite, etc et les autres qui "ont plus de temps à laisser à leur machine à l'exécution" mais ne cherchent pas "le produit qui va se vendre", mais plutôt le produit qui va réfléchir à leur place. Exemple: ce serait complètement stupide de programmer un gagneur automatique au jeu de dames ou au morpion sans utiliser la récursivité.
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • Un exemple concret bidon en pseudo basic :
     10 initialisation
     20 ligne bidon
     30 ligne bidon
     40 si test vérifié alors aller en 20
     50 autre ligne bidon
     60 si test vérifié alors aller en 30
     70 sortie
    

    En pseudo code, en réutilisant la preuve que j'ai mentionnée avant :
    C:=10
    While true :
    
    Switch :
      Case 10 : faire l'initialisation, C:=20
      Case 20 : faire ligne bidon n°20, C:=30
      Case 30 : faire ligne bidon n°30, C:=40
      Case 40 : Si test vérifié, alors C:=20
      Case 50 : faire ligne bidon n°50, C:=60
      Case 60 : Si test vérifié, alors C:=30
      Case 70 : Break
    
    End while.
    

    On peut aussi faire des fonctions mutuellement récursives :
    proc1 ():
       ligne bidon n°20
       Appeler proc2()
    
    proc2 ():
       ligne bidon n°30
       si test est vérifié appeler proc1()
       ligne bidon n°50
       si test est vérifié appeler proc2()
    
    Programme :
       Initialisation
       Appeler proc1()
       Sortie
    

    Je redis en termes plus simples ce que j'ai dit à mon précédent post. Le "goto" ou ses avatars sont plus que défendables, ils sont indispensables en l'état actuel du paradigme informatique. Les théorèmes évoqués par Rescassol ou PR ont des conditions de validité beaucoup trop limitées: ils concernant les bons vieux programmes déterministes qui prennent un truc simple en entrée et renvoient un résultat en sortie.
    FAUX.

    Ce "théorème" ne vaut pas que dans ces conditions limités, confer la preuve que j'ai donné qui marche y compris avec des programmes faisant des trucs plus compliqués que prendre une entrée et rendre une sortie. Ça marche aussi avec les programmes ne terminant jamais, et la preuve s'adapte très facilement au cas parallèle.

    Bref, cessez d'affirmer péremptoirement, sans jamais rien prouver, des choses fausses.
  • Pour info, dans les jeux finis à information parfaite l'algorithme théorique "je me bats moi-même si c'est possible" termine et bat tous les adversaires battables. Autrement dit, il est optimal (et en plus sur le plan philosophique c'est intéressant). C'est pas du tout marrant de se priver de récursivité pour implémenter ça.
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • Je lis en diagonale les 10 regles en puissance, et je me demande bien comment on peut ecrire un logiciel de calcul formel avec ca... pas de recursion, pas d'allocation dynamique de memoire,...

    Le but de ces contraintes est d'avoir des programmes sûrs, sans bug. La NASA n'a pas envie que du code embarqué dans un de leurs engins bugs et entraine la perte de l'appareil.
    Ils imposent donc des contraintes drastiques.

    Des contraintes tout aussi drastiques sont imposées en France dans les avions et les satellites.

    Il faut comprendre qu'écrire un programme pour un PC, ce n'est pas pareil : personne ne va mourir si le PC plante. Si on veut qu'un programme ne plante pas, on n'utilise pas de Goto, ou alors que dans des cas extrêmement règlementés. Même chose pour les while.
    Concernant l'allocation dynamique de mémoire, c'est très dangereux : l'allocation peut échouer s'il n'y a plus de "place". Il vaut mieux gérer toute la mémoire de l'appareil (surtout que sur de l'embarqué, le contexte est connu et bien encadré).
  • christophe c a écrit:
    Il faut reconnaître que dans le privé 99% des programmeurs ne font que ça (des logiciels triviaux accomplissant des tâches pratiques triviales), et se moquent éperdument de ce que permettrait "théoriquement" de faire un ordinateur.
    Force est de constater que dans la vraie vie beaucoup de développeurs-sinon la majorité- sont payés à s'occuper de basses et viles problématiques industrielles-lesdits gens constituant le gros des promotions d'élèves en infomatique. D'où les remarques et les partis pris pédagogiques anti-goto ci-dessus (on va pas en faire un drame, il y a if/then/else, while ...). Quand l'usine à gaz sur laquelle s'affaire le développeur fait des dizaines de milliers de lignes, et que le mec est pas seul (i.e d'autres devront se farcir sa prose), on accueille les garde-fous rédactionnels avec joie.
    Une fonction est un ensemble $f$ de couples tel que pour tous $x,y,z$, si $(x,y)\in f$ et $(x,z)\in f$ alors $y = z$.
  • @PR, mais pourquoi passes-tu ton temps à essayer de "coincer" les gens, en jouant (de mauvaise foi?) sur des virgules (que tu traficotes toi-même). Je le redis, tu as énoncé un théorème qui vaut dans des conditions de validité limitées que j'ai signalées, point barre. Là, tu dis qu'on peut énoncer un autre théorème (moins connu) dans des conditions de validité un tout petit peu élargies, très bien, mais dans ce cas précise de manière formelle ce léger élargissement au lieu de mettre des "FAUX" grandiloquent en majuscules et de prendre des airs doctes. J'ai pris la peine de répondre à une de tes questions (à peine formulée comme telle, c'était du bout des lèvres), de plus la manière** dont tu t'exprimes laisse à penser que tu ne comprends pas très bien ce dont tu parles et te raccroches à des sens de mots très anciens (**quand tu as parlé des type de C, par exemple, alors que le C n'a jamais, en tout cas "de manière usuelle" eu une vocation typage très marquée). Je veux bien te répondre, mais sois précis et questionne. Pour moi "FAUX" n'est pas une question, mais une affirmation. Et je le redis, si tu ne disposes pas de goto ou d'avatars de goto, tu fais baisser strictement la "puissance" d'un langage, et j'ai expliqué pourquoi comme j'ai pu sans rentrer dans la "technique" qui de toute façon n'existe pas vraiment, mais j'ai donné le plus possible de détails. (Tu sembles tellement à côté de la plaque que tu croyais pouvoir réaliser $A\to B$) (avec un $\forall A,B$ devant)
    PR a écrit:
    confer la preuve que j'ai donné qui marche y compris avec des programmes faisant des trucs plus compliqués que prendre une entrée et rendre une sortie. Ça marche aussi avec les programmes ne terminant jamais, et la preuve s'adapte très facilement au cas parallèle.

    Tu n'as vraiment pas suivi, c'est tout de même assez pénible car ce n'est pas la première fois. Tu as donné une preuve hors-sujet utilisant des avatars de goto, tu viens de le refaire et dans un cas extrêmement simple, non parce que tu ne comprends pas mais parce que tu ne suis pas: les intervenants (pas moi!!) qui t'ont succédé ont abordé le sujet des avatars de goto pas juste de goto et lui seul (relis le fil). Par ailleurs, puisque tu te sens capable de prouver "ton théorème" presque pour ainsi dire dans une forme n'exigeant aucun condition de validité, alors poste un programme caml qui réalise (('a->'b)->'a)->'a sans tricher (ie sans "rec", "raise", ou utiliser des fonctions partielles artificielles), puisque c'est de ça qu'il était question. Après tout si tu as une preuve, rien ne t'empêche de la poster.
    PR a écrit:
    cessez d'affirmer péremptoirement, sans jamais rien prouver, des choses fausses

    Mais oui bien sûr, je vais perdre mon temps à poster une preuve du fait que (('a->'b)->'a)->'a n'est pas un théorème de la logique intuitionniste (je n'ai essentiellement rien affirmé de plus) alors que j'ai déjà dû le faire 15 mille fois sur le forum et que ça te prendra montre en main 10mn à retrouver si tu fais un petit effort. Un conseil : lis tout ou sinon sois modéré dans tes réactions. Et uis un petit merci de temps en temps quand on t'apprend des trucs ne serait pas forcément inutile si tu vois ce que je veux dire :-D (même si je respecte que chacun a sa façon de poser des question et que la posture sceptique-procureur a son intérêt)
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • foys a écrit:
    on accueille les garde-fous rédactionnels avec joie.

    D'accord avec toi sur ce point que quand la production devient collective, il faut prendre des précautions. Mais contrairement à ce que disait fdp, je me vois mal (et je ne crois pas que ce soit si fréquent) lire ligne à ligne un code d'un autre même bien écrit. J'ai l'impression que les codes c'est fait pour être compilé et exécuté. Quand la fonction programmée par Bidule cafouille, on la vire du code, point. D'où le développement SURTOUT (me semble-t-il) de techniques de bibliothèques. Mais je me trompe peut-être: peut-être y a-t-il des gens payés à parcourir les centaines de milliers de lignes pour débusquer la petite souris espiègle qui faisait capoter le truc.
    Aide les autres comme toi-même car ils sont toi, ils sont vraiment toi
  • Du reste les avatars de goto ne sont pas goto. Personne n'a dit qu'il fallait interdire "while"...
    Une fonction est un ensemble $f$ de couples tel que pour tous $x,y,z$, si $(x,y)\in f$ et $(x,z)\in f$ alors $y = z$.
  • Bonjour,

    En pas loin de 35 ans de programmation (Pascal, C, C++, C#, Python entre autres),je n'ai jamais utilisé les mots "goto, "continue", "break" (sauf dans un switch) tout simplement parce que j'ai appris comme ça et que j'ai toujours pu m'en passer facilement.
    Par contre, il m'arrive d'écrire "if test return something" dans une fonction, et j'ai bien sûr utilisé la récursivité (quand ça se justifiait) qui ne me semble pas avoir quoi que ce soit avec goto et ses avatars.
    while ne me paraît pas non plus un goto déguisé.
    Et enfin j'ai interdit ces pratiques à mes étudiants pour des raisons pédagogiques.

    Cordialement,

    Rescassol
  • Rescassol a écrit:
    j'ai interdit ces pratiques à mes étudiants pour des raisons pédagogiques

    Tu parles d'une époque (années 1980) où le Basic était encore couramment utilisé, mais est-ce que de nos jours les étudiants abusent encore du goto si on ne leur interdit pas ?
  • Bonjour,

    A cette époque (en 1980), je programmais en Pascal UCSD sur Apple II.
    Mais depuis que j'enseigne la programmation, vers 1984 ou 85 environ, j'ai toujours vu quelques (rares) étudiants me sortir un goto de temps en temps, et encore aujourd'hui.
    Mais il est vrai qu'il n'y en a pas des quantités.

    Cordialement,

    Rescassol
  • Ce n'est qu'une impression mais j'ai cru que Christophe qualifiait d' "avatar de goto" tout ce qui permet de coder une machine de Turing universelle (par opposition à un langage qui ne gèrerait que des fonctions récursives primitives? ) d'où ma remarque sur while.
    Une fonction est un ensemble $f$ de couples tel que pour tous $x,y,z$, si $(x,y)\in f$ et $(x,z)\in f$ alors $y = z$.
  • Bonjour,

    Il me souvient du temps du BASIC (sur diverses plateformes), ou le goto était souvent utilisé dans un if ... then goto ... en lieu et place des boucles while ou repeat, soit parce qu'elles n'existaient pas, soit parce qu'on ne les connaissait pas (eh oui, je fais partie du grand nombre de gens qui ont appris à programmer sans livre), soit parce qu' étant obligé de numéroter les lignes dans certains basic, commencer une boucle à un certain endroit et terminer beaucoup plus loin n'était pas particulièrement agréable à lire. Et puis les éditeurs n'étaient pas aussi pratiques, et les tabulations pour rendre le code agréable à lire ou les commentaires du code tenaient trop de place et étaient peu usités.
    Un banal if ... then goto ... faisait alors le même effet qu'un while ou un repeat (selon l'endroit où il était placé), et était peut-être plus explicite pour un débutant. Conjointement, je programmais à l'époque sur calculatrice (une casio graphique quand j'étais en 6e), et mes premières boucles se faisaient avec des goto et des Lbl.
    Mais ça, c'était avant ! J'ai ensuite appris à m'en passer. Jeter un œil sur divers bouts de code compilés avec un désassembleur est aussi très instructif pour étudier ces histoires de boucles...
  • Pareil j'ai débuté la prog avec un ZX-Spectrum et une Casio PB100 :-D
    J'ai jamais ressenti le besoin de faire un GOTO en 8 ans de R.
Cette discussion a été fermée.
Success message!