Valid XHTML     Valid CSS2    

Décomposition, Conception et Réalisation d'Applications

T.D. 1 : Commentaires et documentations

                    gilles.hunault "at" univ-angers.fr

Table des matières cliquable

  1. Révisons un peu GREP

  2. Révisons un peu AWK

  3. Commentaires

  4. Documentations internes et externes

  5. Documentations et rendus automatiques

  6. Une bonne pratique version 1

  7. Une bonne pratique version 2

  8. Comment ça marche ?

Il est possible d'afficher toutes les solutions via ?solutions=1 et de les masquer via ?solutions=0.

 

1. Révisons un peu GREP

Rappeler les principaux paramètres de grep. En particulier, à quoi servent les paramètres -i et -n ? Pourquoi ne peut-on pas utiliser -h pour avoir l'aide sur la commande grep ?

Si on a défini l'alias suivant : alias 'grep=grep --color=auto -a -i -n' comment avoir accès à la commande grep originale sans supprimer l'alias ?

Comment afficher avec grep la liste des fichiers PHP du répertoire courant qui contiennent l'expression preg_ ?

Comment afficher avec grep la liste des fichiers PHP du répertoire courant qui ne contiennent pas l'expression preg_ ?

A quoi sert zgrep ?

Solution :  

Les options courantes sont certainement -i, -n, -v, -a, -l et -NBRENBRE est un nombre entier.

Voici la liste complète des paramètres pour la version 2.16, sachant que pour la version 3.0, on peut consulter la page de manuel sur le site gnu.org :


     Utilisation : grep [OPTION]... MOTIF [FICHIER]...
     Chercher un MOTIF dans chaque FICHIER ou à partir de l'entrée standard.
     MOTIF est, par défaut, une expression rationnelle simple.
     Exemple : grep -i 'Bonjour, le monde' menu.h main.c
     
     Sélection et interprétation de l'expression rationnelle :
       -E, --extended-regexp     MOTIF est une expression rationnelle étendue
       -F, --fixed-regexp        chaînes séparées par des changements de ligne
       -G, --basic-regexp        MOTIF est une expression rationnelle de base
       -P, --perl-regexp         MOTIF est une expression rationnelle en Perl
       -e, --regexp=MOTIF        utiliser MOTIF comme expression rationnelle
       -f, --file=FICHIER        charger le MOTIF depuis ce FICHIER
       -i, --ignore-case         ignorer la distinction de la casse
       -w, --word-regexp         forcer la correspondance du MOTIF en mots entiers
       -x, --line-regexp         forcer la correspondance du MOTIF en lignes entières
       -z, --null-data           finir les lignes de données par un octet nul
     
     Divers :
       -s, --no-messages         supprimer les messages d'erreur
       -v, --invert-match        sélectionner les lignes sans correspondance
       -V, --version             afficher le nom et la version du logiciel
           --help                afficher l'aide et quitter
     
     Contrôle de la sortie :
       -m, --max-count=NOMBRE    arrêter après NOMBRE correspondances
       -b, --byte-offset         afficher la position en octet avec la sortie
       -n, --line-number         afficher le numéro de ligne avec la sortie
           --line-buffered       vider le tampon après chaque ligne
       -H, --with-filename       afficher le nom de fichier avec les correspondances
       -h, --no-filename         supprimer le préfixe de nom de fichier en sortie
           --label=ÉTIQUETTE     utiliser ÉTIQUETTE pour le préfixe d'entrée standard
       -o, --only-matching       n'afficher que la partie de ligne correspondante
       -q, --quiet, --silent     supprimer toute la sortie standard
           --binary-files=TYPE   considérer que les fichiers binaires sont de type
                                 TYPE : « binary », « text » ou « without-match »
       -a, --text                identique à --binary-files=text
       -I                        identique à --binary-files=without-match
       -d, --directories=ACTION  la façon de traiter les répertoires ;
                                 ACTION est « read », « recurse » ou « skip »
       -D, --devices=ACTION      la façon de traiter les périphériques, les FIFOS
                                 et les sockets ; ACTION est « read » ou « skip »
       -r, --recursive           identique à --directories=recurse
       -R, --dereference-recursive  similaire, mais avec suivi des liens symboliques
           --include=MOTIF_FIC   ne chercher que fichiers correspondants à MOTIF_FIC
           --exclude=MOTIF_FIC   ignorer fichiers et rép. correspondants à MOTIF_FIC
           --exclude-from=FIC    ignorer fichiers correspondants aux motifs de FIC
           --exclude-dir=MOTIF   ignorer les répertoires correspondants au MOTIF
       -L, --files-without-match n'afficher que les fichiers sans correspondance
       -l, --files-with-matches  n'afficher que les fichiers avec correspondances
       -c, --count               n'afficher que le nombre de lignes correspondantes
       -T, --initial-tab         insérer des tabulations (si nécessaire)
       -Z, --null                afficher l'octet nul après le nom de fichier
     
     Contrôle de contexte :
       -B, --before-context=NBRE afficher NBRE lignes de contexte avant
       -A, --after-context=NBRE  afficher NBRE lignes de contexte après
       -C, --context=NBRE        afficher NBRE lignes de contexte en sortie
       -NBRE                     identique à --context=NBRE
           --color[=QUAND],
           --colour[=QUAND]      mettre en évidence les correspondances ; QUAND est
                                 « always » (toujours) « never » (jamais) ou « auto »
       -U, --binary              ne pas enlever les retours chariot en fin de ligne
       -u, --unix-byte-offsets   afficher les positions sans tenir compte des retours
                                 chariot (MS-DOS ou Windows)
     
     « egrep » équivaut à « grep -E ». « fgrep » équivaut à « grep -F ».
     L'invocation directe de « egrep » ou « fgrep » doit être abandonnée.
     Si FICHIER vaut -, lire l'entrée standard. Sans FICHIER, lire . si une option
     -r est donnée, - sinon. Si moins de deux fichiers sont donnés, utiliser -h.
     Le code de sortie est 0 si une ligne est sélectionnée, 1 sinon ;
     en cas d'erreur et si l'option -q n'est pas présente, le code de sortie est 2.
     
     Signalez toute anomalie à : bug-grep@gnu.org
     Page d'accueil de GNU grep : <http://www.gnu.org/software/grep/>
     Aide globale sur les logiciels GNU : <http://www.gnu.org/help/gethelp>
     

Comme on le voit, le paramètre -h a déjà un sens, donc pour voir l'aide, il faut utiliser le paramètre --help même si taper grep -h affiche déjà un début d'aide.

On trouvera ici quelques exemples de commandes grep avec ces paramètres.

Si un alias redéfinit grep, il suffit de préfixer la commande avec un antislah pour retrouver la commande originale. Donc il faut taper \grep au lieu de grep pour être sûr d'utiliser la commande et non pas un alias.

La commande pour afficher la liste des fichiers PHP du répertoire courant qui contiennent l'expression preg_ est grep -l preg_ *php.

La commande pour afficher la liste des fichiers PHP du répertoire courant qui ne contiennent pas l'expression preg_ est grep -l -v "preg_" *.php.

La commande zgrep vient chercher comme grep dans un fichier compressé sans qu'on ait explicitement besoin de le décompresser. Des exemples sont ici.

Une question sans réponse écrite : comment détecter avec grep des lignes vides dans un fichier ?

 

2. Révisons un peu AWK

Comment fonctionne AWK ? Pourquoi parle-t-on d'un langage condition/action ?

Ecrire un script oneliner en AWK pour afficher la ligne 1 de chaque fichier T*txt précédée du nom du fichier.

Ecrire un script oneliner en AWK pour afficher la ligne 1 ou 3 de chaque fichier td*php précédée du nom du fichier.

Ecrire un script AWK pour numéroter les fonctions d'un fichier PHP.

Solution :  

Un script AWK correspond principalement à une suite de tests (conditions) sur le contenu des lignes et de blocs de code (actions) à exécuter lorsque la condition est réalisée. Les autres parties possibles d'un script AWK sont une partie BEGIN exécutée avant la lecture du ou des fichiers, une partie END exécutée après la lecture du ou des fichiers et la définition de sous-programmes.

Pour afficher la ligne 1 de chaque fichier T*txt précédée du nom du fichier, on peut se contenter, dans le terminal, de l'expression

     awk -e ' (FNR==1) { print FILENAME " " $0 } ' T*txt 

Sans passer par AWK, si on est un peu moins regardant sur le formatage de la sortie, la commande Unix head -n 1 T*txt peut aussi convenir.

Pour afficher la ligne 1 ou 3 de chaque fichier td*php précédée du nom du fichier, on peut écrire l'expression

     awk -e ' ((FNR==1)||(FNR==3)) { print FILENAME " " FNR ":" $0 } ' td*php 

Voici le résultat de l'exécution de ces codes :


     $gh> awk -e ' (FNR==1) { print FILENAME " " $0 } ' T*txt
     
     T1.txt La démocratie française n'a plus de sens.
     T2.txt Non aux impérialismes.
     T3.txt La démocratie est basée sur la liberté de penser.
     
     $gh> head -n 1 T*txt
     
     ==> T1.txt <==
     La démocratie française n'a plus de sens.
     
     ==> T2.txt <==
     Non aux impérialismes.
     
     ==> T3.txt <==
     La démocratie est basée sur la liberté de penser.
     
     $gh> awk -e ' ((FNR==1)||(FNR==3)) { print FILENAME " " FNR ":" $0 } ' td*php
     
     td1.php 1:<?php
     td1.php 3:error_reporting(E_ALL | E_NOTICE | E_STRICT ) ;
     td2.php 1:<?php
     td2.php 3:error_reporting(E_ALL | E_NOTICE | E_STRICT ) ;
     td3.php 1:<?php
     td3.php 3:error_reporting(E_ALL | E_NOTICE | E_STRICT ) ;
     td4.php 1:<?php
     td4.php 3:error_reporting(E_ALL | E_NOTICE | E_STRICT ) ;
     td5.php 1:<?php
     td5.php 3:error_reporting(E_ALL | E_NOTICE | E_STRICT ) ;
     td6.php 1:<?php
     td6.php 3:error_reporting(E_ALL | E_NOTICE | E_STRICT ) ;
     

Un script AWK vite fait pour (mal ?) compter et afficher les fonctions d'un fichier PHP est


     /^\s*function / { print nbf++ " " $2 }
     

Mais bien sûr un «beau» script AWK pour compter et afficher les fonctions d'un fichier PHP doit plutôt ressembler à :


     #  cntfuncv2.awk (gH) : compte et affiche les fonctions d'un seul fichier PHP
     
     BEGIN { nbf = 0 ; print "Num Fonction " }
     
     /^\s*function (.*?)\(/ {
       nbf++
       f_nbf = sprintf("%3d",nbf)
       nomf  = substr($2,0,index($2,"(")-1)
       print f_nbf " " nomf
     } # fin de détection du mot function
     
     END { print "\nIl y a donc " nbf " fonctions dans le fichier " FILENAME "\n" }
     

Voici la différence sur un exemple  :


     $gh> awk -f cntfuncv1.awk decra-inc.php | head -n 12
     
     0 tt($chaine)
     1 retourPlanDeCours()
     2 ditCodeSource($nomPage,$nomJs="")
     3 debutCours($numero,$titre="?")
     4 finCours($numero)
     5 debutTDi($numero,$titre="")
     6 finTDi($numero)
     7 debutTP($numero,$titre="")
     8 finTP($numero)
     9 direAfficherSolutionsTD($numSerie)
     10 direAfficherSolutionsTP($numSerie)
     11 solutionTD($numExo,$numSerie)
     
     
     $gh> awk -f cntfuncv2.awk decra-inc.php
     
     Num Fonction
       1 tt
       2 retourPlanDeCours
       3 ditCodeSource
       4 debutCours
       5 finCours
       6 debutTDi
       7 finTDi
       8 debutTP
       9 finTP
     [...]
      16 noSolYet
      17 imgurl
      18 deuxImgurl
     
     Il y a donc 18 fonctions dans le fichier decra-inc.php
     
     

Si vous manquez de pratique sur AWK, le lien suivant peut vous aider : 30 examples for awk.

 

3. Commentaires

Quels sont les symboles usuels pour définir des commentaires, disons en Bash, C++, Javascript et PHP ?

Quelle est la différence entre un commentaire ligne et un commentaire bloc ?

Quelle expression régulière permet de trouver les lignes qui commencent par le symbole # (dièse) ? En déduire une ligne de deux commandes enchainées sous Linux qui compte le nombre de lignes commençant par de tels commentaires dans un fichier Perl. Modifier cette solution pour que le symbole de commentaire puisse être précédé d'un ou plusieurs espaces.

Ecrire un script AWK minimal puis un script "propre" pour donner le pourcentage de lignes commentaires qui commencent par # pour un seul fichier.

A quoi peut servir d'utiliser plusieurs fois le symbole # (dièse) dans les commentaires ?

De façon générale, comment garantir qu'il y aura des commentaires dans un fichier ?

Solution :  

En Bash, un commentaire commence par un dièse. En C++ et Javascript, les commentaires simples commencent par // et ne vont pas après la fin de ligne. Par contre les commentaires complexes peuvent s'étendre sur plusieurs lignes ; ils commencent par /* et se terminent par */. PHP utilise à la fois #, // et le duo /*... */.

Un commentaire ligne ou commentaire simple est en général une indication courte, pour repérer une fin de structure, indiquer un choix, expliciter un nom de variable. Un tel commentaire correspond en général à une seule ligne ou à une partie de ligne (souvent la fin de ligne). Un commentaire bloc (sous-entendu bloc de lignes) correspond souvent à des indications plus longues, des références, un cartouche... On peut aussi parfois s'en servir pour masquer un bloc de code incorrect, incomplet ou long à exécuter.

Voici des exemples de tels commentaires pour un fichier Perl :


     # # (gH)   -_-  extrCml.pl  ;  TimeStamp (unix) : 27 Juin 2018 vers 16:21
     
     
     #################################################################################################
     #                                                                                               #
     #   on consulte une liste de fichiers .xml.gz qu'on décompresse un par un.                      #
     #   pour chaque élément <PC-Compound> d'un fichier XML on crée une ligne                        #
     #   de résultats et on produit au final un fichier .csv pour chaque fichier .xml.gz             #
     #                                                                                               #
     #   attention : on ne conserve que les composés pour lesquel la covalence est 1                 #
     #                                                                                               #
     #################################################################################################
     
     
     ## 1. vérification des paramètres : répertoire liste_des_fichiers info1 [info2 info3...]
     
     use strict  ;
     use POSIX   ;
     
     my $ghVersion = 2.5 ;
     my $checkRes  = 0 ; # mettre 1 pour debug, 0 pour comportement standard
     my $infos     = "names atoms bonds1 bonds2" ;
     
     if ($ARGV[1] eq "") {
       print "\n" ;
       print "extrCml -- version $ghVersion\n\n" ;
       print "  syntaxe  : perl extrCml.pl répertoire liste_des_fichiers info1 [info2...]\n\n" ;
       print "  exemples : perl extrCml.pl data/ listeXml.txt names\n" ;
       print "             perl extrCml.pl inputData ficsCml.lst atoms bonds1 \n\n" ;
       print "  infos à extraire (1 ou plus) : $infos\n" ;
       print "  répertoires associés : Cmp-names, Cmp-atoms, Cmp-bonds1 et Cmp-bonds2\n\n" ;
       exit(-1) ;
     } ; # fin si
     

Le symbole dièse n'est pas considéré comme un caractère particulier pour les expressions régulières. Pour indiquer qu'une chaine commence, on utilise le symbole ^. Donc une ligne qui commence par un commentaire dièse correspond à l'expression régulière /^#/. Si on enchaine grep pour cette regexp et wc, on trouve le nombre de lignes dont le premier caractère est un dièse :


     $gh> grep -n "" cmt-xmp2.pl                   # utilisation de grep pour numéroter les lignes du fichier
     
     1:# fichier cmp-xmp2.pl
     2:use strict ;
     3:use POSIX  ;
     4:my $nom = "Bond, James Bond." ;
     5:   # suite et fin
     6:print "\n$nom\n" ; # fin d'affichage
     7:
     
     $gh> grep -n "#" cmt-xmp2.pl                  # affichage des lignes avec un dièse
     
     1:# fichier cmp-xmp2.pl
     5:   # suite et fin
     6:print "\n$nom\n" ; # fin d'affichage
     
     $gh> grep -n "^#" cmt-xmp2.pl                 # affichage des lignes qui commencent par un dièse
     
     1:# fichier cmp-xmp2.pl
     
     $gh> grep    "^#" cmt-xmp2.pl | wc -l         # nombre de lignes qui commencent par un dièse
     
     1
     
     $gh> grep -c "^#" cmt-xmp2.pl                 # nombre de lignes qui commencent par un dièse (plus court)
     
     1
     

Si on admet que le symbole dièse peut être précédé de zéro, un ou plusieurs espaces, il suffit de rajouter \s* avant le symbole dièse ;


     $gh> grep -n "^\s*#" cmt-xmp2.pl
     1:# fichier cmp-xmp2.pl
     5:   # suite et fin
     

Il est très facile de faire un "script à jeter" en AWK pour calculer le pourcentage de tels commentaires :


     /^\s*#/ { nbCmt++ }
     END     { print "\n" FNR " lignes, " nbCmt " commentaires, soit " sprintf("%6.1f",100*nbCmt/FNR) " %\n" }
     

     $gh> gawk -f pct-cmt1.awk cmt-xmp2.pl
     
     7 lignes, 2 commentaires, soit   28.6 %
     

Mais écrire proprement du code pour pouvoir le relire est une meilleure pratique, même en AWK :


     # comptage des lignes qui commencent par un symbole dièse avec des espaces éventuels devant le dièse
     
     BEGIN   { nbCmt = 0 }
     
     /^\s*#/ { nbCmt++   }
     
     END     { print "\nDans le fichier " FILENAME " il y a " FNR " lignes, dont " nbCmt " commentaires, "
               print "soit un pourcentage de " sprintf("%6.1f",100*nbCmt/FNR) " %\n"
             } # fin de END
     

     $gh> gawk -f pct-cmt2.awk cmt-xmp2.pl
     
     Dans le fichier cmt-xmp2.pl il y a 7 lignes, dont 2 commentaires,
     soit un pourcentage de   28.6 %
     

Utiliser plusieurs dièses permet de définir des commentaires hiérarchiques : par exemple, plus il y a de dièses, plus c'est important :


     $gh> grep -n  "##" listefnsV3.php
     
       8:  ### 1. vérification des paramétres
      35:  ### 2. on cherche les noms de fichiers via les fonctions include_once() et require_once()
      69:  ### 3. affichage numéroté et trié des fichiers é lister avec nombre de lignes
      75:  ##     3.1 tri de la liste via explode/sort/implode
      83:  ##     3.2 affichage de la liste des fichiers
     103:  ### 4. recherche des définitions de fonction
     113:  ##     4.1 on passe en revue tous les fichiers pour trouver function comme mot 1
     135:  ##     4.2 on trie la liste des fonctions et on l'affiche
     153:  ##     4.3 liste par fichier (algorihtme de parcours naif)
     
     

Le système de balisage léger Markdown utilise plusieurs dièses pour produire des titres mais dans l'autre sens, à savoir # pour <h1>, ## pour <h2>, ### pour <h3>... Le lien banc d'essai vous permet de tester du Markdown dans une page Web (avec rendu).

Enfin, pour être sûr d'avoir des commentaires dans un programme, la meilleure technique est sans doute de commencer par mettre les commentaires avec les étapes (comme ci-dessus) dans un fichier vide puis de remplir avec du code, au fur et à mesure, entre les commentaires hiérarchiques.

 

4. Documentations internes et externes

Que signifient les termes UG, PG et RM dans le contexte du développement, de la programmation et de la documentation ?

Et les sigles IG, KB, FAQ ?

A quoi correspondent les expressions documentation interne et documentation externe ?

À quoi sert un (une ?) RFC ?

Dans quelle mesure le schéma actanciel de Greimas permet de mieux comprendre le rôle des différents acteurs/actants et participants d'un logiciel et de sa documentation ? Au sens de la narratologie, peut-on utiliser les termes d'intradiégétique et d'extradiégétique lorsqu'on traite de la documentation d'un logiciel ?

 

Solution :  

Voici, regroupées dans un tableau, la signification de ces termes :

Sigle Anglais Français
UG User's Guide guide de l'utilisateur
PG Programmer's Guide guide du programmeur
RM Reference Manual manuel de référence
RG Reference Guide guide de référence
IG Installation Guide guide d'installation
KB Knowledge base base de connaissances
FAQ Frequently Asked Questions «foire aux questions»

Tous ces documents sont par nature très différents car ils visent des objectifs différents et ils ciblent des publics différents. Ainsi, dans une grande entreprise, l'IG sert au technicien ou à l'ingénieur système pour installer le logiciel, l'UG est à destination des utilisatrices et utilisateurs, le PG ne sera utilisé que par des développeurs ou mainteneurs de l'application. L'ensemble de ces documents fait partie de ce qu'on nomme la documentation logicielle, qui n'est elle-même qu'une composante de l'ingénierie logicielle.

Ce qu'on nomme documentation interne ou documentation inline correspond à ce qu'on peut lire comme commentaires quand on a accès aux codes-sources d'un programme. Par opposition, la documentation externe est la documentation fournie hors code-source, par exemple si le code est compilé et remis seulement sous forme d'exécutable.

Une RFC ou request for comments en anglais est un appel, une demande, une requête à commentaires. C'est un document qui au départ propose ou demande des spécifications. Au final, c'est un document qui présente les spécifications techniques d'un domaine souvent lié à l'informatique. Ainsi RFC6350 décrit ce que contient une VCARD (carte de visite informatisée) et XCARD présente la version XML de VCARD.

La narratologie ou étude scientifique de la narration permet de mieux comprendre les notions de sujet et d'acteur (ou d'actant) dans les structures narratives. Comme dans une certaine mesure une documentation technique peut "raconter" des récits, la plupart du temps sous forme d'exemples, de situations-types, de cas d'utilisation ou de «use case», l'utilisation et l'application des concepts de narratologie aide à prendre du recul et à mieux adapter le vocabulaire et la construction de la documentation en fonction du public visé.

Si le schéma actanciel de Greimas n'est pas utilisable au sens strict, il permet de mieux cerner comment orienter la documentation, pour en faire un outil de communication plutôt qu'un simple document technique.

De la même façon, les termes d' intradiégétique et d' extradiégétique ne sont pas utilisables directement car la documentation logicielle ne correspond exactement à une diégèse. Par contre, la différence induite par l'opposition entre diégèse et mimésis c'est-à-dire entre montrer et raconter permet en informatique de conceptualiser, par exemple pour passer des instances aux classes en programmation objet.

En ce sens, tout «petit commentaire interne» comme # fin si age>18 peut être qualifié d'intradiégétique, alors qu'un commentaire plus général comme /* on utilise ici une instance du design pattern nommé singleton pour garantir l'unicité de... */ peut être assimilé à une action extradiégétique. Au-delà du métalangage et de l'emploi de termes non usuels, faire référence à la narratologie permet aux développeuses et développeurs d'élever le niveau de la discussion et de la documentation grâce aux concepts et points de vue utilisés dans la description.

 

5. Documentations et rendus automatiques

Que désigne le terme de documentation automatique ? S'agit-il vraiment de documentation ?

Comment assure-t-on un rendu automatique dans un page Web, un document PDF etc. lorsqu'il s'agit de documentation technique, éventuellement automatique, en particulier lorsqu'on doit afficher du code informatique et qu'on veut une coloration syntaxique des divers éléments de code ?

Qu'est-ce que Markdown ? et un jupyter notebook ?

Solution :  

La documentation automatique n'existe pas en tant que telle, pas plus que la génération spontanée. Il s'agit en fait de la génération automatique des documents techniques, la plupart du temps à partir de commentaires structurés utilisant un format particulier. On utilise, pour cela, dans le cadre de la documentation logicielle des programmes informatiques, utilisables en ligne de commande ou via une interface utilisateur, nommés générateurs de documentation.

Les grandes solutions classiques se nomment Doxygen, Javadoc, Sphynx. Une comparaison (en anglais) des caractéristiques de telles solutions est ici.

Doxygen, Javadoc et Sphynx sont des solutions suffisamment génériques pour avoir donné naissance à de nombreux autres générateurs, dont Roxygen et phpdocumentor.

Il ne s'agit donc pas vraiment de documentation au sens général du terme (explication) mais plutôt de documentation technique, assimilable à un dictionnaire, une encyclopédie, une liste automatique de définitions.

Il est bien sûr possible de "bricoler " et d'inventer d'autres systèmes de documentation automatique, comme notre système de présentation de fonctions PHP et notre système de présentation de fonctions R.

Pour assurer un rendu Web "propre", avec coloration syntaxique, il est obligatoire de passer par des systèmes automatisés, même si on veut juste afficher le code source d'une page, comme celui de cette page. La tendance des années 2018 est de passer par du balisage léger comme par exemple le système Markdown testable en ligne ici ou . Le premier site interprète aussi le code HTML embarqué comme <font color='blue'>en couleur ici</font> et le code LaTeX embarqué comme $x^2$ alors que le second ne le fait pas.

L'étape suivante consiste à fournir à la fois des explications, du code et de permettre d'exécuter ce code en direct. C'est ce que fournissent les jupyter notebook que l'on peut utiliser dans un navigateur, sans rien installer, comme ici. Il faut bien sûr qu'il y ait de façon sous-jacente un interpréteur interactif qui soit capable de conserver l'environnement d'exécution (variables, fonctions...), ce qu'on nomme kernel (noyau) du notebook. Nous les utiliserons en T.P. avec nos propres notebooks.

Les pages Markdown sont des pages Web simplifiées, dont le but est la transmission rapide des informations à un moindre coût d'écriture des pages. La plupart des systèmes qui fournissent du Markdown offrent la possibilité de générer un PDF à partir de la page, sans surcout d'édition, contrairement à une page Web classique.

Fournir une documentation dans plusieurs formats très différents comme Word, LaTeX et PDF, par exemple, peut se faire avec des systèmes tels que pandoc qu'il est possible de tester sans rien installer ici (le schéma du bas de page est très intéressant). Une démonstration sur machine sera réalisée en TD pour montrer tout le bien qu'on peut penser de tels générateurs (répertoire pandocXmp/).

 

6. Une bonne pratique version 1

On voudrait convertir une quantité (variable quant) de pouces à centimètres et réciproquement (variable unit).

Ecrire le code PHP correspondant aux commentaires à la place des lignes VOTRE CODE ICI pour le texte suivant qui implémente une version 1 de la solution. Que faut-il ajouter pour tester le code en ligne de commandes afin de vérifier la conversion de 3 centimètres ? Recommencer avec du code R. Est-ce plus simple, plus facile ? Le code proposé est-il correct ? Que peut-on en conclure ?


     # conversion pouces/cm et cm/pouces
     # version 1 (gH)
     
     # 1. calcul à partir de quant et unit (variable resultat)
     #    si unit est "pouces" on multiplie quant par 2.54
     #    sinon on divise quant par 2.54
     
     VOTRE CODE ICI
     
     # 2. affichage arrondi (variable arrondi)
     #    on veut produire "3 pouces correspondent à 7.62 cm à 0.01 près"
     #    et "3.1234567*2.54 pouces correspondent à 7.93 cm à 0.01 près"
     
     VOTRE CODE ICI
     

Solution :  

Ecrire la version 1 de la conversion en PHP et en R est très facile à réaliser car, telle que proposée, la conversion se résume à un seul test en si/alors/sinon pour la partie 1 et à un affichage arrondi pour la partie 2 :

Voici le code PHP associé, fichier conv1php.php :


     <?php
     
     # conversion pouces/cm et cm/pouces
     # version 1 (gH)
     
     # 1. calcul à partir de quant et unit (variable resultat)
     #    si unit est "pouces" on multiplie quant par 2.54
     #    sinon on divise quant par 2.54
     
     if ($unit=="pouces") {
       $resultat = $quant*2.54  ;
       $other    = "cm"         ;
     } else {
       $resultat = $quant/2.54  ;
       $other    = "pouces"     ;
     } # fin si
     
     # 2. affichage arrondi  (variable arrondi)
     #    on veut produire "3 pouces correspondent à 7.62 cm à 0.01 près"
     #    et "3.1234567*2.54 pouces correspondent à 7.93 cm à 0.01 près"
     
     $arrondi = sprintf("%0.2f",$resultat) ;
     echo "$quant $unit correspondent à  $arrondi $other à 0.01 près.\n"
     
     ?>
     

Pour tester ce code, il suffit d'initialiser les variables impliquées et d'importer les instructions précédentes. Voici le code PHP correspondant, fichier testConv1php.php :


     <?php
     
     # vérification minimale de la conversion pouces/cm
     
     $quant = 3    ;
     $unit  = "cm" ;
     
     include("conv1php.php") ;
     
     ?>
     

L'exécution (minimaliste) en ligne de commandes semble correcte  :


     $gh> php testConv1php.php
     
     3 cm correspondent à  1.18 pouces à 0.01 près.
     

Pour mettre au point le code, on aurait pu se contenter de l'interpréteur PHP en ligne de commandes :


     $gh> php -a
     Interactive mode enabled
     
     php> include("conv1php.php") ; # ne fonctionnera pas, les variables ne sont pas initialisées
     
     PHP Notice:  Undefined variable: unit in /home/gh/public_html/Decra/conv1php.php on line 10
     PHP Stack trace:
     PHP   1. {main}() php shell code:0
     PHP   2. {main}() php shell code:0
     PHP Notice:  Undefined variable: quant in /home/gh/public_html/Decra/conv1php.php on line 14
     PHP Stack trace:
     PHP   1. {main}() php shell code:0
     PHP   2. {main}() php shell code:0
     PHP Notice:  Undefined variable: quant in /home/gh/public_html/Decra/conv1php.php on line 23
     PHP Stack trace:
     PHP   1. {main}() php shell code:0
     PHP   2. {main}() php shell code:0
     PHP Notice:  Undefined variable: unit in /home/gh/public_html/Decra/conv1php.php on line 23
     PHP Stack trace:
     PHP   1. {main}() php shell code:0
     PHP   2. {main}() php shell code:0
       correspondent à  0.00 pouces à 0.01 prés.
     
     php> $quant = 3    ;
     
     php> $unit  = "cm" ;
     
     php> include("conv1php.php") ;
     
     3 cm correspondent à  1.18 pouces à 0.01 prés.
     
     php> exit
     

Le code R est bien sûr très similaire, puisqu'on traduit le même algorithme. Le voici, dans le fichier conv1r.r :


     # conversion pouces/cm et cm/pouces
     # version 1 (gH)
     
     # 1. calcul à partir de quant et unit (variable resultat)
     #    si unit est "pouces" on multiplie quant par 2.54
     #    sinon on divise quant par 2.54
     
     if (unit=="pouces") {
       resultat <- quant*2.54
       other    <- "cm"
     } else {
       resultat <- quant/2.54
       other    <- "pouces"
     } # fin si
     
     # 2. affichage arrondi (variable arrondi)
     #    on veut produire "3 pouces correspondent à 7.62 cm à 0.01 près"
     #    et "3.1234567*2.54 pouces correspondent à 7.93 cm à 0.01 près"
     
     arrondi <- sprintf("%0.2f",resultat)
     cat(quant,unit,"correspondent à",arrondi,other,"à 0.01 près.\n")
     

De même pour l'appel de ce code en R, fichier testConv1r.r :


     # vérification minimale de la conversion pouces/cm
     
     quant <- 3
     unit  <- "cm"
     
     source("conv1r.r",encoding="latin1")
     

Là encore, l'exécution minimaliste semble correcte  :


     $gh> R --vanilla --quiet --slave --encoding=latin1 --file=testConv1r.r
     
     3 cm correspondent à 1.18 pouces à 0.01 près.
     

Et on aurait aussi pu commencer à tester avec l'interpréteur R :


     $gh> R --vanilla
     
     R version 3.4.4 (2018-03-15) -- "Someone to Lean On"
     [...]
     
     > source("conv1r.r",encoding="latin1") # ne fonctionnera pas, les variables ne sont pas initialisées
     Error in eval(ei, envir) : objet 'unit' introuvable
     
     > quant <- 3
     
     > unit  <- "cm"
     
     > source("conv1r.r",encoding="latin1")
     
     3 cm correspondent à 1.18 pouces à 0.01 près.
     
     > q()
     

Le code n'est bien sûr pas correct parce qu'il ne traite pas bien l'unité. Il suffit de mettre le mot "dollar" comme unité pour s'en rendre compte.

Ce qu'on peut en conclure :

  • Qu'importe le langage, si l'algorithme est faux, le résultat est faux.

  • Tous les langages n'ont pas besoin de point-virgule pour terminer [certaines] instructions.

  • Il faut penser à tester tous les cas afin de fournir des codes robustes.

  • Une fois l'algorithme écrit, les scripts sont assez faciles à écrire et sont très similaires.

 

7. Une bonne pratique version 2

Ecrire le code PHP correspondant aux commentaires à la place des lignes VOTRE CODE ICI pour le texte suivant. Que faut-il ajouter pour tester le code en ligne de commandes ? Recommencer avec du code R. Est-ce plus simple, plus facile ? Que peut-on en conclure ?

Recommencer avec du code Python. Est-ce plus simple, plus facile ? Que peut-on en conclure globalement ?

Que manque-t-il pour disposer d'une «bonne» fonction de conversion ?


     # conversion pouces/cm et cm/pouces
     # version 2 (gH)
     
     fonction conv(quant,unit)
     
     # 1. essai de calcul à partir de quant et unit (variable resultat)
     #    on initialise convOk à 0
     #    si unit est "pouces" on multiplie quant par 2.54 et on met 1 dans convOk
     #    si unit est "cm"     on divise    quant par 2.54 et on met 1 dans convOk
     
     VOTRE CODE ICI
     
     # 2. affichage arrondi si l'unité était correcte comme dans l'algorithme précédent
     #    prévoir un affichage si unité incorrecte
     
     VOTRE CODE ICI
     
     # 3. renvoi du resultat exact et du status de conversion
     
     renvoyer(resultat,convOk)
     
     fin_fonction # conv
     

Solution :  

Après réflexion, la version 1 de la conversion est très incomplète et surtout dangereuse car si on tape autre chose que pouces alors la machine considère que l'unité est cm. Par exemple, si on écrit pouce c'est-à-dire sans s à la fin, l'algorithme se trompe. La version 2 vient donc assurer une conversion correcte pour les deux unités indiqués. De plus elle implémente une fonction, ce qui est ici un bon choix.

Voici le nouveau code PHP, fichier conv2php.php :


     <?php
     error_reporting(E_ALL | E_NOTICE | E_STRICT ) ;
     
     # conversion pouces/cm et cm/pouces
     # version 2 (gH)
     
     function conv($quant=-1,$unit="?") {
     
     # 1. essai de calcul à partir de quant et unit (variable resultat)
     #    on initialise convOk à 0
     #    si unit est "pouces" on multiplie quant par 2.54 et on met 1 dans convOk
     #    si unit est "cm"     on divise    quant par 2.54 et on met 1 dans convOk
     
     $convOk   =  0 ;
     $resultat = -1 ;
     
     if ($unit=="pouces") {
       $resultat = $quant*2.54  ;
       $other    ="cm" ;
       $convOk   = 1   ;
     } # fin si
     
     if ($unit=="cm") {
       $resultat = $quant/2.54  ;
       $other    = "pouces" ;
       $convOk   = 1        ;
     } # fin si
     
     # 2. affichage arrondi si l'unité était correcte comme dans l'algorithme précédent
     #    prévoir un affichage si unité incorrecte
     
     if ($convOk==1) {
       $arrondi = sprintf("%0.2f",$resultat) ;
       echo "$quant $unit correspondent à  $arrondi $other à 0.01 près.\n" ;
     } else {
       echo "unité '$unit' incorrecte ; unités reconnues : cm et pouces.\n" ;
     } # fin si
     
     # 3. renvoi du resultat exact et du status de conversion
     
     return(array($resultat,$convOk)) ;
     
     } # fin_fonction # conv
     
     ?>
     

Pour tester ce nouveau code, il n'y a plus à initialiser des variables mais à fournir des valeurs dans l'appel de la fonction. Sans être exhaustif, le code de test vient vérifier le comportement dans les 3 cas usuels : bonne unité cm, bonne unité pouces, mauvaise unité. Voici le code PHP correspondant, fichier testConv2php.php :


     <?php
     error_reporting(E_ALL | E_NOTICE | E_STRICT ) ;
     
     # vérification de la conversion pouces/cm
     
     include("conv2php.php") ;
     
     # tests série1 : tout est OK pour conv avec cm comme avec pouces
     
     conv(3,"pouces") ;
     
     # avec récupération des valeurs renvoyées :
     
     list($quantConv,$resConv) = conv(1,"cm") ;
     
     # tests série2 : détection de l'erreur sur unit
     
     conv(2,"dollar") ;
     
     ?>
     

L'exécution en ligne de commandes montre le comportement attendu par rapport à ces trois tests :


     3 pouces correspondent à  7.62 cm à 0.01 près.
     1 cm correspondent à  0.39 pouces à 0.01 près.
     unité 'dollar' incorrecte ; unités reconnues : cm et pouces.
     

Sans plus de commentaires, voici la version 2 en R, fichier conv2r.r :


     # conversion pouces/cm et cm/pouces
     # version 2 (gH)
     
     conv <- function(quant=-1,unit="?") {
     
     # 1. essai de calcul à partir de quant et unit (variable resultat)
     #    on initialise convOk à 0
     #    si unit est "pouces"     on multiplie quant par 2.54 et on met 1 dans convOk
     #    si unit est "cm"         on divise    quant par 2.54 et on met 1 dans convOk
     
     convOk   <-  0
     resultat <- (-1)
     
     if (unit=="pouces") {
       resultat <- quant*2.54
       other    <- "cm"
       convOk   <- 1
     } # fin si
     
     if (unit=="cm") {
       resultat <- quant/2.54
       other    <- "pouces"
       convOk   <- 1
     } # fin si
     
     # 2. affichage arrondi si l'unité était correcte comme dans l'algorithme précédent
     #    prévoir un affichage si unité incorrecte
     
     if (convOk==1) {
       arrondi <- sprintf("%0.2f",resultat)
       cat(quant,unit,"correspondent à",arrondi,other,"à 0.01 près.\n")
     } else {
       cat("unité '",unit,"' incorrecte ; unités reconnues : cm et pouces.\n",sep="")
     } # fin si
     
     # 3. renvoi du resultat exact et du status de conversion
     
     return(invisible(c(resultat,convOk)))
     
     } # fin_fonction # conv
     

Et le code R pour la tester, fichier testConv2r.r :


     # vérification de la conversion pouces/cm
     
     source("conv2r.r",encoding="latin1")
     
     # tests série1 : tout est OK pour conv avec cm comme avec pouces
     
     conv(3,"pouces")
     
     # avec récupération des valeurs renvoyées :
     
     resConv <- conv(1,"cm")
     
     # tests série2 : détection de l'erreur sur unit
     
     conv(2,"dollar")
     

L'exécution est quasiment identique :


     $gh> R --vanilla --quiet --slave --encoding=latin1 --file=testConv2r.r
     
     3 pouces correspondent à 7.62 cm à 0.01 près.
     1 cm correspondent à 0.39 pouces à 0.01 près.
     unité 'dollar' incorrecte ; unités reconnues : cm et pouces.
     

Il pourrait être tentant d'écrire le même code en Python, soit le fichier conv2py.py suivant  :


     # -*- coding:latin1 -*-
     # conversion pouces/cm et cm/pouces
     # version 2 (gH)
     
     def conv(quant=-1,unit="?") :
     
        # 1. essai de calcul à partir de quant et unit (variable resultat)
        #    on initialise convOk à 0
        #    si unit est "pouces" on multiplie quant par 2.54 et on met 1 dans convOk
        #    si unit est "cm"     on divise    quant par 2.54 et on met 1 dans convOk
     
        convOk   =  0
        resultat = -1
     
        if (unit=="pouces") :
          resultat = quant*2.54
          other    = "cm"
          convOk   = 1
        # fin si
     
        if (unit=="cm") :
          resultat = quant/2.54
          other    = "pouces"
          convOk   = 1
        # fin si
     
        # 2. affichage arrondi si l'unité était correcte comme dans l'algorithme précédent
        #    prévoir un affichage si unité incorrecte
     
        if (convOk==1) :
          arrondi = "%0.2f" % resultat
          print(quant,"pouces correspondent à",arrondi,"cm é 0.01 prés.")
        else :
          print("unité '",unit,"' incorrecte ; unités reconnues : cm et pouces.")
        # fin si
     
        # 3. renvoi du resultat exact et du status de conversion
     
        return([resultat,convOk])
     
     # fin_fonction conv
     

et d'utiliser un autre fichier pour les tests, le fichier testConv2py.py suivant  :


     # -*- coding:latin1 -*-
     
     from conv2py import conv
     
     # tests série1 : tout est OK pour conv avec cm comme avec pouces
     
     conv(3,"pouces")
     
     # avec récupération des valeurs renvoyées :
     
     resConv = conv(1,"cm")
     
     # tests série2 : détection de l'erreur sur unit
     
     conv(2,"dollar")
     
     

pour obtenir les mêmes résultats :


     $gh> python3 testConv2py.py
     
     3 pouces correspondent à 7.62 cm à 0.01 prés.
     1 pouces correspondent à 0.39 cm à 0.01 prés.
     unité ' dollar ' incorrecte ; unités reconnues : cm et pouces.
     

mais ce serait mal connaitre Python car Python dispose d'un mécanisme d'auto-test :


     # -*- coding:latin1 -*-
     # conversion pouces/cm et cm/pouces
     # version 3 (gH)
     
     def conv(quant=-1,unit="?") :
     
        ''' conv(quant,unit) : conversion pouces/cm avec affichage arrondi '''
     
        # 1. essai de calcul à partir de quant et unit (variable resultat)
        #    on initialise convOk à 0
        #    si unit est "pouces" on multiplie quant par 2.54 et on met 1 dans convOk
        #    si unit est "cm"     on divise    quant par 2.54 et on met 1 dans convOk
     
        convOk   =  0
        resultat = -1
     
        if (unit=="pouces") :
          resultat = quant*2.54
          other    = "cm"
          convOk   = 1
        # fin si
     
        if (unit=="cm") :
          resultat = quant/2.54
          other    = "pouces"
          convOk   = 1
        # fin si
     
        # 2. affichage de arrondi à 0.01 près si l'unité est correcte
        #    un message d'erreur est affiché si unité est incorrecte
     
        if (convOk==1) :
          arrondi = "%0.2f" % resultat
          print(quant,"pouces correspondent à",arrondi,"cm é 0.01 prés.")
        else :
          print("unité '",unit,"' incorrecte ; unités reconnues : cm et pouces.",sep="")
        # fin si
     
        # 3. renvoi du resultat exact et du status de conversion
     
        return([resultat,convOk])
     
     # fin_fonction conv
     
     # auto-test
     
     if __name__ == "__main__" :
     
         # not run: help(conv)
     
         # tests série1 : tout est OK pour conv avec cm comme avec pouces
     
         conv(3,"pouces")
     
         # avec récupération des valeurs renvoyées :
     
         resConv = conv(1,"cm")
     
         # tests série2 : détection de l'erreur sur unit
     
         conv(2,"dollar")
     
     # fin de l'auto-test
     

Ce qu'on peut en conclure :

  • Utiliser une fonction pour gérer du code qui calcule des valeurs est une bonne idée.

  • Il est prudent, si le langage l'autorise, de mettre des valeurs par défaut.

Ce qu'on peut en conclure globalement :

  • Le langage n'est pas le plus important. Ce qui compte surtout, c'est l'analyse.

  • C'est souvent quand on écrit les tests qu'on voit ce qu'on a oublié de programmer.

Ce qu'il manque encore pour disposer d'une bonne fonction de conversion :

  • Un paramètre pour rendre l'affichage facultatif afin de pouvoir utiliser la fonction dans un autre programme sans avoir d'affichage.

  • Puisqu'on utilise des langages non typés explicitement, il faudrait tester le type du premier paramètre afin de garantir qu'il s'agit bien d'un nombre.

  • Le nommage des unités n'est pas très «propre». Il serait bon d'accepter pouce comme unité (donc sans s), et d'accepter aussi cms comme unité (donc avec un s). On pourrait aussi envisager d'accepter aussi c et p comme unité.

 

8. Comment ça marche ?

Essayer d'expliquer la conception (hors interface Web) du mini-système documentaire disponible dans les pages système de présentation de fonctions PHP et système de présentation de fonctions R.

Combien de temps vous faudrait-il pour refaire ces deux systèmes ? Et pour les généraliser à n'importe quel code des langages classiques ?

Solution :  

 

Vous n'êtes pas autorisé(e) pour l'instant à consulter la solution.

Des indications seront fournies en TD. N'hésitez pas à venir !

 

       retour au plan de cours  

 

Code-source PHP de cette page ; code Javascript associé.

 

retour gH    Retour à la page principale de   (gH)