[couverture de Linux Magazine 72]

Goto Perl

Article publié dans Linux Magazine 72, mai 2005.

Copyright © 2005 - Philippe Bruhat et Jean Forget

[+ del.icio.us] [+ Developers Zone] [+ Bookmarks.fr] [Digg this] [+ My Yahoo!]

Chapeau de l'article

Depuis vos premiers cours de programmation (en C ou en Pascal), on vous serine de ne pas utiliser goto. Mais connaissez-vous la raison de cet interdit dogmatique ? Cet article vous expliquera d'où vient le goto, pourquoi personne ne l'aime et comment l'utiliser. En Perl.

L'histoire de goto

On trouve une commande goto dans presque tous les langages de programmation (FORTRAN, Algol, COBOL, SNOBOL, BASIC, C, C++, Perl...). Le principe de ces commandes est de faire sauter l'exécution du programme du point où se trouve le goto au point indiqué en paramètre. En BASIC, ce sera un numéro de ligne, mais dans la plupart des langages (qui n'ont pas de lignes numérotées) le pointeur de programme saute à un endroit indiqué par une étiquette (label en anglais).

Le saut dans un flot d'instructions est une instruction tellement importante qu'elle existait déjà à l'âge de la programmation en langage machine. On la trouve encore dans tous les micro-processeurs. En assembleur, le mnémonique correspondant est généralement JMP (pour jump) ou BRA (pour branch).

Les GO TO de FORTRAN

En FORTRAN, il existe plusieurs formes de GO TO : le GO TO classique (unconditional GO TO), le GO TO calculé (computed GO TO) et le GO TO assigné (assigned GO TO).

Note : Les explications que je donne dans les paragraphes suivants sont valables pour FORTRAN 77. Les programmes d'exemple ont été testés avec f77, le compilateur FORTRAN 77 du projet GNU.

Le cas de COBOL

COBOL se distingue non pas par son GO TO, mais par son instruction ALTER, qui permet en quelque sorte d'établir une déviation. Voici un exemple :

    * Début du programme, toutes les déclarations,
    * définitions et autres. Verbeux comme c'est
    * l'habitude en COBOL
    
    ALTER etiq1 TO GO TO etiq2.
    
    * quelques centaines de lignes de code, également verbeuses
    
    GO TO etiq1.
    
    * encore quelques centaines de lignes
    
    etiq1.
      DISPLAY "salut" UPON CONSOLE.
      GO TO suite.
    etiq2.
      DISPLAY "coucou" UPON CONSOLE.
      GO TO suite.
    
    * et la suite

Comme vous l'avez deviné, ce n'est pas salut qui s'affichera à l'écran. Mais l'auriez-vous deviné si j'avais réellement listé le source complet du programme ? Cela dit, l'utilisation de ALTER n'est pas monnaie courante...

Les langages de programmation sans goto

Il existe des langages de programmation où le goto a été laissé de côté ou, peut-être, oublié. En voici quelques uns, que les auteurs ont utilisés, le plus souvent de façon éphémère.

Vous connaissez le shell. Vous avez déjà écrit quelques scripts pour enchaîner quelques commandes qui reviennent régulièrement. Certains de vos scripts utilisent même des variables, des boucles et des tests camouflés en expressions logiques avec && et || ou des tests explicites avec if then elif else fi. Mais aviez-vous remarqué qu'il n'existe aucune instruction goto ? (Avez-vous au moins lu la page de man ?)

Un autre langage incontournable pour quiconque utilise Unix de façon un peu poussée, c'est awk. Là aussi, lisez la page de manuel ou un tutoriel, vous ne verrez jamais mentionnée d'instruction goto.

Emacs est basé sur le langage E-lisp et il comporte donc un interpréteur E-lisp incorporé ainsi que la documentation du langage. Celui des deux coauteurs qui utilise Emacs pour taper cet article l'a vérifié, la documentation ne fait référence à aucun branchement goto donc cette instruction est vraisemblablement interdite (il existe des instructions dont le nom comporte goto, mais il s'agit de déplacer le pointeur de lecture dans le tampon en cours d'édition, pas de modifier la position du pointeur d'instruction). Quant aux autres variantes de Lisp et aux langages dérivés, qui n'ont même pas l'excuse d'être incorporés (embedded) dans un éditeur de texte, ils ignorent complètement ce mot et ce concept.

Plus curieux : le langage de programmation des calculatrices HP-48 ne comporte aucun goto. Pourtant, la HP-48 est l'héritière de la HP-41 dans laquelle le goto (orthographié GTO) était indispensable dès qu'un programme devait effectuer un traitement itératif.

Si vous grepez la documentation de Ruby à la recherche d'un GOTO, vous verrez que c'est un nom ou un prénom assez répandu au Japon, mais que ce n'est pas une instruction du langage.

En Java, goto est un mot réservé, mais l'instruction ne fait pas partie du langage.

Edsger W. Dijkstra

Edsger W. Dijkstra
(Photo Hamilton Richards)

Edsger Wybe Dijkstra (1930-2002) est un chercheur en informatique hollandais qui a eu un rôle important dans le monde de la recherche informatique. Il a travaillé sur le premier compilateur ALGOL 60, et est l'auteur d'un algorithme qui porte son nom pour le problème du plus court chemin (vous pouvez trouvez la description de l'algorithme de Dijkstra dans l'article Recherche des chemins optimaux pour les jeux vidéo, de Fabrice Rossi, dans GNU/Linux Magazine 53).

L'article de 1968 sur goto a eu une influence considérable : il a accéléré la disparition du goto des habitudes de programmation, fait progresser la notion de programmation structurée et amené la création des structures de contrôle telles que les boucles while.

La polémique

En 1968, Dijkstra a lancé un pavé dans la mare, remettant en question l'ordre établi et proposant de faire table rase du goto. Dijkstra a publié un papier intitulé A case against the goto statement, mais que que Wirth, alors rédacteur en chef des Communications of the ACM a changé en courrier au rédacteur en chef, en lui donnant par la même occasion un titre destiné à devenir célèbre, Go To Statement Considered Harmful. Dans cet article, Dijkstra constatait que les goto nuisent à la compréhension du programme par un lecteur humain et que la qualité du code varie en sens inverse de la densité des goto. Une petite remarque pour ceux qui voudraient lire cet article : ne vous attardez pas sur la partie médiane de l'article, qui décrit un concept d'index textuel et d'index dynamique. Je doute fort qu'un quelconque programmeur ait eu recours à cette méthode pour décortiquer le fonctionnement d'un programme. L'essentiel est écrit au début et à la fin de l'article, le goto est indésirable et il doit disparaître.

Un peu plus tard, Donald Knuth a publié un autre article, Structured Programming with go to Statements. Le titre donne l'impression d'être un pamphlet destiné à répondre avec véhémence à l'article de Dijkstra. En lisant l'article, on constate que ce n'est pas du tout le cas. Knuth le dit lui-même : il ne prend pas partie dans la guerre sainte des partisans et des adversaires du goto, il veut simplement raconter comment il programme et quelle place le goto a dans son code. Il ajoute que les adversaires du goto liront des arguments allant dans le sens de l'éradication du goto et que les partisans du goto liront des arguments justifiant l'existence du goto. Et effectivement, à la lecture de son article, on peut constater que Knuth aborde le sujet avec un esprit ouvert et serein, sans idée préconçue et sans œillères.

L'article de Knuth montre qu'il n'y a pas besoin d'insérer des goto pour obtenir un programme illisible. Il montre comment émuler les goto avec une boucle et une structure conditionnelle et il fait remarquer à juste titre que le programme obtenu est encore moins lisible que le programme d'origine avec des goto (construction due à Jacopini). Remarquons que Knuth réfute ainsi la partie médiane de l'article de Dijkstra en montrant que le système d'index textuels et dynamiques ne permet pas à un humain de mieux comprendre le fonctionnement du programme.

Les deux gourous ont tiré des conclusions différentes, que je présenterai au moyen d'une métaphore. Pour Dijkstra, puisque certaines personnes se sont intoxiquées en ingérant de l'eau de Javel, il faut interdire totalement la commercialisation de ce produit et le réserver aux chimistes professionnels. Pour Knuth, il faut vendre l'eau de Javel dans des récipients avec des bouchons de sécurité tels qu'un enfant de moins de 7 ans ne puisse pas l'ouvrir et il faut que les parents instruisent leurs enfants pour leur présenter les dangers de l'eau de Javel et les précautions d'emploi.

Quand on y réfléchit bien, nous sommes entourés d'ustensiles et d'engins qui, mal utilisés, sont dangereux voire létaux mais qui, dans des conditions normales d'utilisation, sont inoffensifs ou presque : couteaux, ciseaux à bouts pointus, tondeuses, prises électriques, etc. Pour les programmeurs, il ne faut pas oublier d'ajouter goto à la liste. ;-)

Le folklore

La notoriété de l'article de Dijkstra a eu un effet secondaire, l'expression considered harmful qui, comme nous l'avons vu, n'est pas due à Dijkstra mais à Wirth, a été maintes fois et est finalement passée dans le langage courant (des informaticiens, s'entend). Si vous googlez cette expression, vous obtenez 65 000 pages, dont plus de 6 000 si vous restreignez la recherche aux titres. Dans deux cas, les auteurs ont voulu élever le débat au niveau supérieur, avec "Goto statement considered harmful" considered harmful, publié par l'ACM et cité dans le Jargon File et "Considered harmful" essays considered harmful, disponible sur le web.

Et puisque le GO TO est mauvais, l'inverse doit être bon ! C'est ce que Lawrence Clark a dû se dire lorsqu'il a spécifié une nouvelle instruction, le COME FROM. C'est également ce que se sont dit les auteurs d'INTERCAL, lorsqu'ils ont effectivement implémenté le COME FROM. À noter que Clark avait également spécifié le computed COME FROM et le assigned COME FROM.

En 1987, l'un des lauréats de l'IOCCC (International Obfuscated C Code Contest) fut Spencer Hines, grâce à un programme qui contenait une bonne vingtaine de goto. De plus, ce programme déclarait quelques variables entières toog, togo, oogt, ootg, otog, des variables chaînes de caractères toog, ogto, tgoo et ainsi de suite. Quant aux labels vers lesquels pointent les goto, je n'ai pas besoin de vous faire un dessin. Et finalement, le style d'indentation mérite d'être appelé « style », mais pas « indentation ».

To go or not to go?

Supprimer les goto

Puisque programmer avec des goto est si néfaste, il a fallu proposer d'autres solutions aux programmeurs pour qu'ils arrêtent de cuisiner du spaghetti code. Nous allons vous en présenter quelques unes, que vous avez certainement déjà rencontrées.

Les utilisations légitimes de goto

goto est utilisé à plusieurs endroits du noyau Linux (et dans de nombreux autres systèmes et logiciels complexes). Est-ce que les développeurs du kernel sont de mauvais programmeurs ? Non, bien sûr ; goto est souvent le seul moyen de se sortir de cas complexes, typiquement pour la gestion d'erreurs.

Les analyseurs syntaxiques (et les machines à états) sont aussi un cas d'utilisation légitime de goto. Nous vous invitons par exemple à examiner le code du module HTML::Parser 2.25 (disponible sur CPAN), dernière version pur Perl de cet analyseur HTML (les suivantes sont en XS, pour des raisons de performance).

De nos jours, il existe des alternatives structurées aux utilisations légitimes de goto les plus courantes. Aux constructions de boucles while ou until sont associées les instructions break, redo ou continue qui permettent de quitter la boucle par le début ou la fin, et d'effectuer un traitement à chaque tour de boucle, même si celle-ci a été interrompue.

Perl et goto

You can also "goto hell" if you like, which will of course work better if you've defined the label "hell".

-- Larry Wall, The Perl Conference, 20 août 1997.

Quand on y réfléchit, qu'est-ce qu'un goto ? C'est un saut à un autre point du programme, en général conditionné par un test. Ceci se fait sans aucune initialisation (contrairement à l'appel d'un sous-programme). Le test de la condition ne fait bien sûr pas partie du goto.

Nous allons donc nous intéresser aux différentes manières de sauter d'un point à un autre d'un programme Perl, sans initialisation, ce qui exclut les appels à des sous-programmes et l'utilisation de références à des fonctions (tables de distribution).

Les « goto structurés »

Dans de nombreux cas de traitements en boucle, on souhaite pouvoir définir d'autres points de sortie que la fin de la boucle, répéter une itération sans tester la condition de sortie ou encore sortir de plusieurs niveaux de boucles imbriquées d'un seul coup.

En Perl, les instructions next, last et redo permettent de résoudre ces différents cas de figure sans faire appel au goto ni introduire tests et variables inutiles dans le corps de la boucle.

Ces instructions sont toutes des genres de goto, puisqu'elles permettent de sauter à diverses positions dans la boucle (ou juste en dehors). Le bloc continue associé à la boucle while permet quant à lui d'assurer que la partie finale de la boucle sera exécutée même quand on la court-circuite.

Quand nous parlons de « genres de goto », il s'agit bien d'une comparaison, car comme le précise la documentation de Perl : A loop LABEL is not actually a valid target for a goto; it's just the name of the loop. (L'étiquette d'une boucle n'est pas une cible valide pour un goto, il s'agit juste du nom de la boucle.)

La commande goto

Malgré l'existence des commandes de sortie de boucle, une commande goto a tout de même son utilité. C'est ainsi qu'il existe en fait trois formes de goto en Perl.

Exemples pratiques

Maintenant que nous avons exposé l'histoire et les alternatives au goto, voici quelques exemples pratiques d'utilisation.

Une routine de recherche

Supposons que l'on cherche l'adresse de divers correspondants. Ces adresses sont stockées à différents niveaux de mémoire, dans une variable du programme, dans un fichier se trouvant sur la machine locale, dans une base de données accessibles par le réseau local ou sur Internet. Dans un but de performances, on privilégiera l'accès le plus rapide.

    use DBI;
    use WWW::Search::PagesJaunes;
    my $repert    = '/home/moi/adresses.txt';
    my $db_source = '???';
    my $db_user   = 'moi';
    my $db_passwd = 's3kr3t';
    my %adresse;

    sub recherche {
        my ($qui) = @_;

        # Cas idéal, il est déjà en mémoire
        return $adresse{$qui} if $adresse{$qui};

        # Fichier local, peut-être ?
        open REPERT, $repert or goto ESSAI_BDD;
        while (<REPERT>) {
            my ( $lui, $ou ) = split ':';
            # Si trouvé, on mémorise et on renvoie
            return $adresse{$lui} = $ou if $lui eq $qui;
        }
        close REPERT;

      ESSAI_BDD:

        my $dbh = DBI->connect( $data_source, $db_user, $db_passwd )
          or goto ESSAI_INTERNET;
        my $acces =
          $dbh->prepare("SELECT adresse FROM personnes WHERE nom = ?");
        return $dbh->selectrow_array($acces)
          or goto ESSAI_INTERNET;

      ESSAI_INTERNET:
        
        my $pj = WWW::Search::Pagesjaunes->new();
        $pj->find( nom => $qui, departement => "75" );
        my @resultats = $pj->results();
        return $resultats[0]->addresse if @resultats;

    }

Cet exemple montre l'un des cas d'utilisation admise du goto : la reprise après erreur. Cette fonction est lisible pour deux raisons : tous les goto descendent et la fonction tient sur une page d'écran (nous sommes désolés si la pagination du magazine fait que ce n'est pas le cas sur le papier).

La variante sans goto aurait conduit à utiliser un indicateur booléen et à enrober chacun des accès élémentaires read, fetch ou get par un test vérifiant le succès de l'accès précédent open, connect.

En fait, on peut également se passer complètement de l'utilisation de goto pour cet exemple, en utilisant des tables de distribution (dispatch tables, voir l'article Perles de Mongueurs à ce sujet dans GNU/Linux Magazine 65). Cette technique a l'avantage d'être plus facilement extensible.

    my %adresse;

    # table de distribution des méthodes utilisables
    # l'ordre alphabétique des clés permet de définir
    # l'ordre de préférence des techniques de recherche
    my %recherche = (
        r00_memoire  => sub { return $adresse{$_[0]} } # déclaration en ligne
        r01_local    => \&recherche_fichier,           # fonctions définies
        r02_bdd      => \&recherche_bdd,               # un peu plus loin
        r03_internet => \&recheche_internet,           # dans le script 
    );

    sub recherche {
        my ($qui) = @_;
        my $adresse;

        for my $essai ( sort keys %recherche) {
            $adresse = $recherche{$essai}->( $qui );
            last if $adresse;
        }

        # technique de cache pour accélerer les recherches suivantes :
        # on enrichit le cache local
        $adresse{$qui} = $address if defined $adresse;

        return $adresse;
    }

    # recherche dans un fichier
    my $repert = '/home/moi/adresses.txt';
    sub recherche_fichier {
        open my $fh, $repert or return;
        while (<$fh>) {
            my ( $lui, $ou ) = split ':';
            return $ou if $lui eq $qui;
        }
        close $fh;
    }

    # recherche dans une base de données
    use DBI;
    my $db_source = '???';
    my $db_user   = 'moi';
    my $db_passwd = 's3kr3t';
    sub recherche_bdd {
        # même code que dans l'exemple précédent
    }

    # recherche sur Internet
    use WWW::Search::PagesJaunes;
    sub recherche_internet {
        # même code que dans l'exemple précédent
    }

L'ajout d'une nouvelle technique de recherche se cantonnera donc à l'ajout d'une fonction dans le code et d'une clé dans la table de hachage. Pour changer l'ordre des tentatives, il suffit de renommer les clés de la table de hachage.

Un analyseur lexical et syntaxique

Nous prenons un exemple inspiré d'un programme réel conçu par l'un des coauteurs. Je travaillais sur un projet pour écrire des programmes C/SQL qui devaient être déployés dans plusieurs pays. Dès l'origine, il a été convenu de scinder le source de chaque programme en au moins deux fichiers, toto.c qui contient les traitements et toto.h qui contient quelques déclarations du genre :

    #define MSG "C'est planté !"
    char *url = "http://www.mongueurs.net/";
    char *actions  [] = { "PUT", "GET", "POST" };
    char *histoire [] = { "Il demanda : \"Quand ?\"\n",
    	    "Elle répondit : \"Demain...\x22\n" };

Et c'est à moi qu'est revenue la tâche de spécifier les utilitaires permettant d'extraire les chaînes à traduire et de réinjecter les chaînes traduites. Comme les fichiers en entrée obéissaient à une syntaxe C très simplifiée, l'extraction pouvait se baser sur un automate à quatre états (dans la réalité un peu plus, les fichiers pouvant contenir des commentaires). Voici ce que cela donne en Perl :

    #!/usr/local/bin/perl
    #
    # Extraction des libellés d'un source C simplifié
    #

    use strict;
    use warnings;
    local $/ = \1; # lecture caractère par caractère
    my $ident;
    my $chaine;
    my $car;
    open IN, $ARGV[0] or die "Problème avec le fichier";

    A:
      $car = <IN>;
      goto FIN unless defined $car;
      if ($car =~ /[[:alnum:]]/)
        { $ident = $car; goto B }
      if ($car eq '"')
        { $chaine = ''; goto C }
      goto A;

    B:
      $car = <IN>;
      goto FIN unless defined $car;
      if ($car =~ /[[:alnum:]]/)
        { $ident .= $car; goto B }
      if ($car eq q("))
        { $chaine = ''; goto C }
      goto A;

    C:
      $car = <IN>;
      goto D if $car eq q(\\);
      if ($car eq q("))
        { store($ident, $chaine); goto A }
      $chaine .= $car; goto C;

    D:
      $car = <IN>;
      $chaine .= q(\\) . $car; goto C;

    FIN:
      print "Fini !\n";

    sub store {
      my ($ident, $chaine) = @_;
      print "$ident -> $chaine\n" # En fait un ordre SQL
    }

Au fait, nous vous avions promis un exemple de la méthode de Jacopini pour éliminer les GOTO. Voici ce que donnerait cette méthode :

    #!/usr/local/bin/perl
    #
    # Extraction des libellés d'un source C simplifié
    # avec la méthode de Jacopini
    #

    use strict;
    use warnings;
    local $/ = \1; # caractère par caractère
    my $ident;
    my $chaine;
    my $car;
    open IN, $ARGV[0] or die "Problème avec le fichier";
    my $etiq = 'A';
    while ($etiq ne 'FIN')
      {
        if ($etiq eq 'A')
          {
            $car = <DATA>;
            if (not defined $car)
              { $etiq = 'FIN' }
            elsif ($car =~ /[[:alnum:]]/)
              { $ident = $car; $etiq = 'B' }
            elsif ($car eq '"')
              { $chaine = ''; $etiq = 'C' }
            else
              { $etiq = 'A' }
          }
        if ($etiq eq 'B')
          {
            $car = <DATA>;
            if (not defined $car)
    	      { $etiq = 'FIN' }
            elsif ($car =~ /[[:alnum:]]/)
              { $ident .= $car; $etiq = 'B' }
            elsif ($car eq q("))
              { $chaine = ''; $etiq = 'C' }
            else
    	      { $etiq = 'A' }
          }
        if ($etiq eq 'C')
          {
            $car = <DATA>;
            if ($car eq q(\\))
              { $etiq = 'D' }
            elsif ($car eq q("))
              { store($ident, $chaine); $etiq = 'A' }
            else
              { $chaine .= $car; $etiq = 'C' }
          }
        if ($etiq eq 'D')
          {
            $car = <DATA>;
            $chaine .= q(\\) . $car; $etiq = 'C';
          }
      }
    print "Fini !\n";

    sub store {
      my ($ident, $chaine) = @_;
      print "$ident -> $chaine\n"
    }

Vous serez je pense d'accord avec Dijkstra, Knuth et nous-mêmes, cette version est illisible !

La méthode AUTOLOAD

Voici un exemple d'utilisation d'AUTOLOAD pour définir le plus tard possible (à l'exécution) le code d'une fonction. C'est le cas usuel d'utilisation du goto routine en Perl.

L'exemple de code ci-après est utilisé en production pour définir des accesseurs associés à des valeurs extraites d'une base de donnée. Une table rights définit des associations user_id, right_id, où l'identificateur du droit est une simple chaîne de caractères (admin, support, etc.). Les méthodes is_admin(), is_support(), etc. sont utilisées dans d'autres parties du code et dans des templates pour déterminer ce à quoi l'utilisateur connecté a accès.

    sub AUTOLOAD {

        # notre classe n'a pas de méthode DESTROY
        return if $AUTOLOAD =~ /::DESTROY/;

        # la fonction rights() renvoie la liste des droits
        # associés à cet utilisateur dans la base
        if( $AUTOLOAD =~ /::is_(\w+)$/ ) {
            my $attr = $1;
            no strict 'refs';

            # crée la méthode et l'insère dans la table des symboles
            # du paquetage en cours (celui de la classe)
            *{$AUTOLOAD} = sub { $_[0]->rights()->{$attr} };

            # appel la méthode de façon transparente
            goto &{$AUTOLOAD};
        }

        # meurt si une méthode inconnue a été appelée
        croak "AUTOLOAD: Unknown method $AUTOLOAD";
    }

Avec cette technique, les méthodes d'accès aux droits n'ont pas a être ajoutées au fur et à mesure qu'on invente de nouvelles catégories d'utilisateurs, et on garantit qu'elles sont toutes codées de la même façon.

Conclusion

Vous l'avez constaté, il existe de nombreux cas en programmation où l'instruction goto est appropriée. Mais depuis la polémique autour de l'utilisation du goto les langages de programmation ont adopté de nouveaux mots-clés pour faciliter la programmation structurée.

C'est pourquoi en Perl, le goto « classique » est réservé à certains types de programmes et ne doit pas être utilisé pour la sortie de boucles, la gestion d'exception ou l'émulation d'une instruction case.

Le goto routine de Perl n'est pas couvert par les réticences d'Edsger Dijkstra : c'est un moyen très courant de manipuler la pile d'appels, qui est souvent utilisé en programmation objet en Perl.

Références

goto

Langages de programmation

Auteurs

Annexe

Si vous avez lu l'article de Knuth, vous n'avez peut-être pas prêté attention à l'expression « four-letter words like goto » utilisée par Knuth. Moi, si. En effet, elle faisait écho à un article que j'ai écrit en d'autres circonstances pour un autre public.

En dehors de la programmation Perl, je pratique les jeux de simulation historique (wargames). La plupart sont en anglais, mais on trouve parfois des traductions pour certains. À force de lire des mauvaises traductions (dont certaines venaient de ma plume :-(), j'ai décidé de mettre par écrit un certain nombre de conseils concernant la traduction de l'anglais vers le français, conseils que l'on n'apprend pas toujours au collège ou au lycée.

Pour l'extrait suivant, vous aurez peut-être besoin de revoir la première heure de l'Étoffe des Héros et de réviser votre Histoire de France. Ou bien, de chercher les noms propres dans votre moteur de recherche favori (méfiez-vous des homonymes : cherchez « général Pierre Koenig » dans les pages en français).

Si vous avez vu l'Étoffe des Héros, vous vous souvenez peut-être de Pancho Barnes, la tenancière du saloon de la base d'Edwards, un personnage haut en couleurs. Dans son autobiographie, Chuck Yeager la décrit ainsi :

V.O. Pancho would never use a five- or six-letter word when a four-letter word would do. She had the filthiest mouth that any of us fighter jocks had ever heard.

J'ai vu ces phrases traduites ainsi :

Mauvais Elle n'employait jamais un mot de cinq ou six lettres quand un mot de quatre lettres pouvait suffire. Elle avait un langage ordurier auquel même nous, vieux pilotes de chasse, nous n'arrivions pas à nous faire.

La traduction correcte serait plutôt :

Bon Elle n'employait jamais un mot de six ou sept lettres quand un mot de cinq lettres pouvait suffire. Elle avait un langage ordurier auquel même nous, vieux pilotes de chasse, nous n'arrivions pas à nous faire.

Et pourquoi ? Parce que pour les Français, les termes « un mot de cinq lettres » ne désignent pas n'importe quel mot de la longueur indiquée, mais un mot bien particulier. Vous savez, le « mot de Cambronne », que le général Koenig, assiégé à Bir-Hakeim, aurait prononcé quand on lui proposait de se rendre. De même, l'expression « four-letter word » désigne pour les Américains, non pas un mot, mais une série de mots à usage... disons, spécialisé. Cela va du « Nuts » anodin du général McAuliffe, assiégé à Bastogne en 1944 (1) aux deux mots S**t et F**k que vous connaissez bien, en passant par d'autres que je ne vous enseignerai pas. Par conséquent, la véritable traduction de « four-letter word » est « mot de cinq lettres ». Par extension, dans la phrase citée en exemple, il faudra traduire « five or six » par « six ou sept ».

(1) Comme quoi, les généraux commandant des troupes d'élite (Vieille Garde, Légion Étrangère, Parachutistes) ont parfois les mêmes idées. À propos, quelqu'un peut-il m'indiquer quel mot grossier fut proféré par Fingon, le Haut-Roi des Noldor, encerclé à la bataille des Larmes Innombrables ? ;-)

Pour en revenir à la phrase de Knuth, le nombre de lettres n'a aucun intérêt. La traduction qui s'impose serait donc : « des mots obscènes comme goto ».

[IE7, par Dean Edwards] [Validation du HTML] [Validation du CSS]