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
3. Commentaires
4. Documentations internes et externes
5. Documentations et rendus automatiques
6. Une bonne pratique version 1
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 : afficher la solution
Les options courantes sont certainement -i, -n, -v, -a, -l et -NBRE où NBRE 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 : afficher la 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*txtSans 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*phpVoici 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.phpSi 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 : afficher la 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 siLe 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) 1Si 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 finIl 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 : afficher la 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 : afficher la 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 là. 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 ICISolution : afficher la 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> exitLe 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 # convSolution : afficher la 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 # convEt 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 convet 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-testCe 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 : afficher la solution
Vous n'êtes pas autorisé(e) pour l'instant à consulter la solution.
Des indications seront fournies en TD. N'hésitez pas à venir !
Code-source PHP de cette page ; code Javascript associé.
Retour à la page principale de (gH)