7. Programmation : interaction, structures de contrôle,
scripts, fonctions, entrées-sorties fichier, debugging

7.1 Généralités

Les "M-files" sont des fichiers au format texte (donc "lisibles") contenant des instructions MATLAB/Octave et portant l'extension *.m. On a vu la commande diary (au chapitre "Workspace") permettant d'enregistrer un "journal de session" qui, mis à part l'output des commandes, pourrait être considéré comme un M-file. Mais la manière la plus efficace de créer des M-files (c'est-à-dire "programmer" en langage MATLAB/Octave) consiste bien entendu à utiliser un éditeur de texte ou de programmation.

On distingue fondamentalement deux types de M-files : les scripts (ou programmes) et les fonctions. Les scripts travaillent dans le workspace, et toutes les variables créées/modifiées lors de l'exécution d'un script sont donc visibles dans le workspace et accessibles ensuite interactivement ou par d'autres scripts. Les fonctions, quant à elles, n'interagissent avec le workspace ou avec le script-appelant principalement via leurs "paramètres" d'entrée/sortie, les autres variables manipulées restant internes (locales) aux fonctions.

MATLAB/Octave est un langage interprété (comme les langages Perl, Python, Ruby, PHP, les shell Unix...), c'est-à-dire que les M-files (scripts ou fonctions) n'ont pas besoin d'être préalablement compilés avant d'être utilisés (comme c'est le cas des langage classiques C/C++, Java, Fortran...). A l'exécution, des fonctionnalités interactives de debugging et de profiling permettent d'identifier les bugs et optimiser le code.

MATLAB et Octave étant de véritables "progiciels", le langage MATLAB/Octave est de "haut niveau" et offre toutes les facilités classiques permettant de développer rapidement des applications interactives évoluées. Nous décrivons dans les chapitres qui suivent les principales possibilités de ce langage dans les domaines suivants :

7.2 Éditeurs

Les M-files étant des fichiers-texte, il est possible de les créer et les éditer avec n'importe quel éditeur de texte/programmation de votre choix. Idéalement, celui-ci devrait notamment offrir des fonctionnalités d'indentation automatique, de coloration syntaxique...

7.2.1 Commandes relatives à l'édition

Pour créer ou éditer un M-file depuis la fenêtre de commande MATLAB/Octave et basculer dans l'éditeur :
edit M-file   ou   edit('M-file')   ou   open M-file   ou   open('M-file')
Bascule dans l'éditeur et ouvre le M-file spécifié. Si celui-ci n'existe pas, il est proposé de créer un fichier de ce nom (sauf sous MATLAB où open retourne une erreur).
Il est obligatoire de passer un nom de fichier à la commande open. S'agissant de edit, si l'on ne spécifie pas de M-file cela ouvre une fenêtre d'édition de fichier vide.

Sous MATLAB et sous Octave GUI, c'est l'éditeur intégré à ces IDE's qui est bien entendu utilisé. Si vous utilisez MATLAB ou Octave-CLI en ligne de commande dans une fenêtre terminal, voyez dans les chapitres qui suivent comment spécifier quel éditeur doit être utilisé.

Depuis les interfaces graphiques MATLAB et Octave GUI, on peut bien entendu aussi faire :

7.2.2 Éditeur/débugger intégré à MATLAB

MATLAB fournit un IDE complet comportant un éditeur (illustration ci-dessous) offrant également des possibilités de debugging.


Éditeur intégré de MATLAB R2014 (ici sous Windows)

Quelques fonctionnalités pratiques de l'éditeur intégré de MATLAB :

Pour utiliser un autre éditeur, définissez-le avec la commande setenv('EDITOR','path/editeur')

7.2.3 Éditeurs pour GNU Octave

Éditeur/débugger intégré à Octave GUI

Avec l'apparition de l'interface graphique Octave GUI (Octave Graphical User Interface) depuis Octave ≥ 3.8, on dispose également d'un IDE complet comportant un éditeur permettant de faire de la coloration syntaxique, autocomplétion, debugging (définition de breakpoints, exécution step-by-step...).


Éditeur intégré de GNU Octave 4.0 (ici sous Windows)

Quelques fonctionnalités pratiques de l'éditeur intégré de Octave GUI :

Autres éditeurs pour Octave

Il est cependant possible (et nécessaire lorsqu'on utilise Octave en ligne de commande depuis une fenêtre terminal) d'utiliser d'autres éditeurs de programmation. La situation dépend du système d'exploitation (voir notre chapitre "Installation/configuration GNU Octave") : On spécifie quel éditeur doit être invoqué lorsque l'on travaille avec Octave-CLI (i.e. lorsque l'on passe la commande edit) avec la fonction built-in EDITOR('path/editeur') que l'on insère généralement dans son prologue .octaverc
Ex: Le morceau de script multi-plateforme ci-dessous teste sur quelle plateforme on se trouve et redéfinit ici "Gedit" comme éditeur par défaut dans le cas où l'on est sous Linux :
if ~isempty(findstr(computer,'linux'))
  EDITOR('gedit')        % définition de l'éditeur par défaut
  edit('mode','async')   % exécuter la commande "edit" de façon détachée
else
  % on n'est pas sous Linux, ne rien faire de particulier
end

Système Éditeur conseillé Définition de l'éditeur
    (pour prologue .octaverc)
Indenter à droite,
    désindenter à gauche
Commenter,
    décommenter
Multiplateforme Atom EDITOR('atom') Edit>Lines>Indent (ou tab, ou alt-cmd-6)
Edit>Lines>Outdent (ou maj-tab, ou alt-cmd-5)
Edit>Toggle Comments (ou maj-cmd-7)
Windows Notepad++ EDITOR
('path/notepad++.exe')
Edit>Indent>Increase (ou tab)
Edit>Indent>Decrease (ou maj-tab)
Edit>Comment/Uncom.> Toggle Block Comment (ou ctrl-Q)
Linux Gedit (GNOME) EDITOR('gedit') tab

maj-tab

Edit>Comment Code (ou ctrl-M)
Edit>Uncomment Code (ou ctrl-maj-M)
(voir cependant ci-dessous)
macOS TextWrangler EDITOR('edit') Text>Shift Right (ou cmd-])
Text>Shift Left (ou cmd-[)
Il est nécessaire d'élaborer un "script TextWrangler"...



Éditeur de programmation libre Notepad++ sous Windows

Conseils relatifs à l'éditeur Gedit sous Linux

En premier lieu, enrichissez votre éditeur Gedit par un jeu de "plugins" supplémentaires déjà packagés : Activation de la coloration syntaxique : Affichage des numéros de lignes : via Edit > Preferences, puis dans l'onglet "View" activer "Display line numbers"

Pour pouvoir mettre en commentaire un ensemble de lignes sélectionnées :

Fermeture automatique des parenthèses, crochets, acollades, apostrophes ... : en activant simplement le plugin "Bracket Completion"

Affichage des caractères spéciaux tab, espace ... : en activant (et configurant) le plugin "Draw Spaces"

Pour automatiser certaines insertions (p.ex. structures de contrôles...) :

7.3 Interaction écran/clavier, warnings et erreurs

Pour être en mesure de développer des scripts MATLAB/Octave interactifs (affichage de messages, introduction de données au clavier...) et les "débugger", MATLAB et Octave offrent bon nombre de fonctionnalités utiles décrites dans ce chapitre.

7.3.1 Affichage de texte et de variables

disp(variable)
disp('chaîne')
Affiche la variable ou la chaîne de caractère spécifiée. Avec cette commande, et par oposition au fait de frapper simplement variable, seul le contenu de la variable est affiché et pas son nom. Les nombres sont formatés conformément à ce qui a été défini avec la commande format (présentée au chapitre "Fenêtre de commande").

Ex: les commandes M=[1 2;3 5] ; disp('La matrice M vaut :') , disp(M) produisent l'affichage du texte "La matrice M vaut :" sur une ligne, puis celui des valeurs de la matrice M sur les lignes suivantes

{count=} fprintf('format', variable(s)...)
{count=} printf('format', variable(s)...)
Affiche, de façon formatée, la(les) variable(s) spécifiées (et retourne facultativement le nombre count de caractères affichés). Cette fonction ainsi que la syntaxe du format, repris du langage de programmation C, sont décrits en détails au chapitre "Entrées-sorties".
L'avantage de cette méthode d'affichage, par rapport à disp, est que l'on peut afficher plusieurs variables, agir sur leur formatage (nombre de chiffres après le point décimal, justification...) et entremêler texte et variables sur la même ligne de sortie.

Ex: si l'on a les variables v=444; t='chaîne de car.';, l'instruction fprintf('variable v= %6.1f et variable t= %s \n',v,t) affiche, sur une seule ligne : "variable v= 444.0 et variable t= chaîne de car."


7.3.2 Affichage et gestion des avertissements et erreurs, beep

Gestion d'erreurs et avertissements


3:30 min
Gestion d'erreurs et avertissements

Les erreurs sont des évènements qui provoquent l'arrêt d'un script ou d'une fonction, avec l'affichage d'un message explicatif.
Les avertissements (warnings) consistent en l'affichage d'un message sans que le déroulement soit interrompu.

warning( {'id',} 'message')
warning( {'id',} 'format', variable(s)...)
Affiche le message spécifié sous la forme "warning: message", puis continue (par défaut) l'exécution du script ou de la fonction.
Le message peut être spécifié sous la forme d'un format (voir spécification des "Formats d'écriture" au chapitre "Entrées-sorties"), ce qui permet alors d'incorporer une(des) variable(s) dans le message !
L'identificateur id du message prend la forme composant{:composant}:mnémonique , où :
  - le premier composant spécifie p.ex. le nom du package
  - le second composant spécifie p.ex. le nom de la fonction
  - le mnémonique est une notation abrégée du message
L'identificateur id est utile pour spécifier les conditions de traitement de l'avertissement (voir ci-dessous).
Sous Octave, une description de tous les types de warnings prédéfinis est disponible avec help warning_ids
struct = warning
Passée sans paramètre, cette fonction indique de quelle façon sont traités les différents types de messages d'avertissements (warnings). Les différents états possibles sont :
    on= affichage du message d'avertissement, puis continuation de l'exécution
    off= pas d'affichage de message d'avertissement et continuation de l'exécution
    error= condition traitée comme une erreur, donc affichage du message d'avertissement puis interruption !
warning('on|off|error', 'id' )
Changement de la façon de traiter les avertissements du type id spécifié. Voir ci-dessus la signification des conditions on, off et error. On arrive ainsi à désactiver (off) certains types d'avertissements, les réactiver (on), ou même les faire traiter comme des erreurs (error) !
warning('query', 'id' )
Récupère le statut courant de traitement des warnings de type id
{string=}lastwarn
Affiche (ou récupère sur la variable string) le dernier message d'avertissement (warning)
      Ex:
X=123; S='abc'; warning('Demo:test','X= %u et chaine S= %s', X, S)
  affiche l'avertissement : 'warning: X= 123 et chaîne S= abc'
• puis si l'on fait warning('off','Demo:test')
  et que l'on exécute à nouveau le warning ci-dessus, il n'affiche plus rien
• puis si l'on fait warning('error','Demo:test')
  et que l'on exécute à nouveau le warning ci-dessus, cela affiche cette fois-ci une erreur : 'error: X vaut: 123 et la chaîne S: abc'

error('message')
error('format', variable(s)...)
Affiche le message indiqué sous la forme "error: message", puis interrompt l'exécution du script ou de la fonction dans le(la)quel(le) cette instruction a été placée, ainsi que l'exécution du script ou de la fonction appelante. Comme pour warning, le message peut être spécifié sous la forme d'un format, ce qui permet alors d'incorporer une(des) variable(s) dans le message.

Sous Octave, si l'on veut éviter qu'à la suite du message d'erreur soit affiché un "traceback" de tous les appels de fonction ayant conduit à cette erreur, il suffit de terminer la chaîne message par le caractère "newline", c'est-à-dire définir error("message... \n"). Mais comme on le voit, la chaîne doit alors être définie entre guillemets et non pas entre apostrophes, ce qui pose problème à MATLAB. Une façon de contourner ce problème pour faire du code portable pour Octave et MATLAB est de définir error(sprintf('message... \n'))

Remarque générale : Lorsque l'on programme une fonction, si l'on doit prévoir des cas d'interruption pour cause d'erreur, il est important d'utiliser error(...) et non pas disp('message'); return, afin que les scripts utilisant cette fonction puissent tester les situations d'erreur (notamment avec la structure de contrôle try...catch...end).

{string=}lasterr
Affiche (ou récupère sur la variable string) le dernier message d'erreur
beep
Émet un beep sonore

7.3.3 Entrée d'information au clavier

a) variable=input('prompt') ;
b) chaîne=input('prompt', 's') ;
MATLAB/Octave affiche le prompt ("invite") spécifié, puis attend que l'utilisateur entre quelque-chose au clavier terminé par la touche enter

a) En l'absence du paramètre 's', l'information entrée par l'utilisateur est "interprétée" (évaluée) par MATLAB/Octave, et c'est la valeur résultante qui est affectée à la variable spécifiée. L'utilisateur peut donc, dans ce cas, saisir une donnée de n'importe quel type et dimension (nombre, vecteur, matrice...) voire toute expression valide !
On peut, après cela, éventuellement détecter si l'utilisateur n'a rien introduit (au cas où il aurait uniquement frappé enter) avec : isempty(variable), ou length(variable)==0
b) Si l'on spécifie le second paramètre 's' (signifiant string), le texte entré par l'utilisateur est affecté tel quel (sans évaluation) à la variable chaîne indiquée. C'est donc cette forme-là que l'on utilise pour saisir interactivement du texte.

Dans les 2 cas, on place généralement, à la fin de cette commande, un ; pour que MATLAB/Octave "travaille silencieusement", c'est-à-dire ne quittance pas à l'écran la valeur qu'il a affectée à la variable.

      Ex:
• la commande v1=input('Entrer v1 (scalaire, vecteur, matrice, expression, etc...) : ') ; affiche "Entrer v1 (scalaire, vecteur, matrice, expression, etc...) : " puis permet de saisir interactivement la variable numérique "v1" (qui peut être de n'importe quel type/dimension)
• la commande nom=input('Entrez votre nom : ', 's') ; permet de saisir interactivement un nom (contenant même des espace...)
a) pause
b) pause(secondes)
a) Lorsque le script rencontre cette instruction sans paramètre, il effectue une pause, c'est-à-dire attend que l'utilisateur frappe n'importe quelle touche au clavier pour continuer son exécution.
b) Si une durée secondes est spécifiée, le script reprend automatiquement son exécution après cette durée.
Sous MATLAB, on peut passer la commande pause off pour désactiver les éventuelles pauses qui seraient effectuées par un script (puis pause on pour rétablir le mécanisme des pauses).

7.4 Structures de contrôle

Structures de contrôle, traitement d'erreurs


19:17 min
Les structures de contrôle sont un aspect fondamental de tous les langages de programmation. Il s'agit de constructions permettant d'exécuter des blocs d'instructions de façon itérative (boucle) ou sous condition (test). Nous présentons dans cette vidéo :
• les boucles for, while et do/until, et l'effet dans celles-ci des instructions break et continue
• le test if/elsif/else
• la construction switch/case
• le traitement d'erreurs avec try/catch, et les fonctions warning et error
Mais avant de programmer des boucles (coûteuses en temps d'exécution), on réfléchira cependant toujours à deux fois, sous MATLAB/Octave (langages permettant le traitement natif de tableaux de N-dimension sans implémenter de boucles) s'il n'est pas possible de s'en passer, en programmant plutôt des expressions tirant parti des possibilités vectorisées de MATLAB/Octave (y.c. l'indexation logique qu'on présentera dans une prochaine vidéo), ce qui permet des gains de temps considérables lorsqu'on manipule de gros tableaux (millions d'éléments).

7.4.1 Présentation des structures de contrôle

Les "structures de contrôle" sont, dans un langage de programmation, des constructions permettant d'exécuter des blocs d'instructions de façon itérative (boucle) ou sous condition (test). MATLAB/Octave offre les structures de contrôle de base typiques présentées dans le tableau ci-dessous et qui peuvent bien évidemment être "emboîtées" les unes dans les autres. Notez que la syntaxe est différente des structures analogues dans d'autres langages (C, Java, Python).

Comme MATLAB/Octave permet de travailler en "format libre" (les caractères espace et tab ne sont pas significatifs), on recommande aux programmeurs MATLAB/Octave de bien "indenter" leur code, lorsqu'ils utilisent des structures de contrôle, afin de faciliter la lisibilité et la maintenance du programme.

Octave propose aussi des variations aux syntaxes présentées plus bas, notamment : endfor, endwhile, endif, endswitch, end_try_catch, ainsi que d'autres structures telles que:
      unwind_protect body... unwind_protect_cleanup cleanup... end_unwind_protect
Nous vous recommandons de vous en passer pour que votre code reste portable !

Structure Description
Boucle for...end

for var = tableau
   instructions...
end
Considérons d'abord le cas général où tableau est une matrice 2D (de nombres, de caractères, ou cellulaire... peu importe). Dans ce cas, l'instruction for parcourt les différentes colonnes de la matrice (c'est-à-dire matrice(:,i)) qu'il affecte à la variable var qui sera ici un vecteur colonne. À chaque "itération" de la boucle, la colonne suivante de matrice est donc affectée à var, et le bloc d'instructions est exécuté.

Si tableau est un vecteur, ce n'est qu'un cas particulier :
• dans le cas où c'est un vecteur ligne (p.ex. une série), la variable var sera un scalaire recevant à chaque itération l'élément suivant de ce vecteur
• si c'est un vecteur colonne, la variable var sera aussi un vecteur colonne qui recevra en une fois toutes les valeurs de ce vecteur, et le bloc d'instructions ne sera ainsi exécuté qu'une seule fois puis on sortira directement de la boucle !

Si l'on a tableau à 3 dimensions, for examinera d'abord les colonnes de la 1ère "couche" du tableau, puis celles de la seconde "couche", etc...

Ex:

  • for n=10:-2:0 , n^2, end affiche successivement les valeurs 100 64 36 16 4 et 0
  • for n=[1 5 2;4 4 4] , n, end affiche successivement les colonnes [1;4] [5;4] et [2;4]
Boucle parfor...end

parfor (var=deb:fin {, max_threads})
   instructions...
end
Variante simplifiée mais parallélisée de la boucle for...end :
• le bloc d'instructions est exécuté parallèlement en différents threads (au maximum max_threads) sur plusieurs cores de CPU, l'ordre d'itération n'étant cependant pas garanti
deb et fin doivent être des entiers, et fin > deb
Boucle while...end
("tant que" la condition n'est pas fausse)

while expression_logique
   instructions...
end
Si l'expression_logique est satisfaite, l'ensemble d'instructions spécifiées est exécuté, puis l'on reboucle sur le test. Si elle ne l'est pas, on saute aux instructions situées après le end.
On modifie en général dans la boucle des variables constituant l'expression_logique, sinon la boucle s'exécutera sans fin (et on ne peut dans ce cas en sortir qu'avec un ctrl-C dans la fenêtre console !)

S'agissant de l'expression_logique, cela ne doit pas nécessairement être un scalaire ; ce peut aussi être un tableau de dimension quelconque, et dans ce cas la condition est considérée comme satisfaite si tous les éléments ont soit la valeur logique True soit des valeurs différentes de 0. A contrario elle n'est pas satisfaite si un ou plusieurs éléments ont la valeur logique False ou la valeur 0.

Ex:

  • n=1 ; while (n^2 < 100) , disp(n^2) , n=n+1 ; end affiche les valeurs n^2 depuis n=1 et tant que n^2 est inférieur à 100, donc affiche les valeurs 1 4 9 16 25 36 49 64 81 puis s'arrête
Boucle do...until
("jusqu'à ce que" une condition se vérifie)

do
   instructions...
until expression_logique
Propre à Octave, cette structure de contrôle classique permet d'exécuter des instructions en boucle tant que l'expression_logique n'est pas satisfaite. Lorsque cette expression est vraie, on sort de la boucle. Voir plus haut au chapitre while...end ce qu'il faut entendre par expression_logique.

La différence par rapport à la boucle while est que l'on vérifie la condition après avoir exécuté au moins une fois le bloc d'instructions.

Pour atteindre le même but sous MATLAB, on pourrait faire une boucle sans fin de type while true instructions... end à l'intérieur de laquelle on sortirait par un test et l'instruction break (voir plus bas).

Test if...elseif...else...end
("si, sinon si, sinon")

  if expression_logique_1
     instructions_1...      
{ elseif expression_logique_i
     instructions_i...      }
{ else
     autres_instructions... }
  end
Si l'expression_logique est satisfaite, le bloc instructions_1 est exécuté, puis on saute directement au end sans faire les éventuels autres tests elseif.
Sinon (si l'expression_logique_1 n'est pas satisfaite), MATLAB/Octave examine tour à tour les éventuelles clauses elseif. Si l'une d'entre elles est satisfaite, le bloc instructions_i correspondant est exécuté, puis on saute directement au end (sans tester les autres clauses elseif).
Si aucune expression_logique n'a été satisfaite et qu'on a défini une clause else, le bloc autres_instructions correspondant est exécuté.

Noter que les blocs elseif ainsi que le bloc else sont donc facultatifs !
Voir plus haut au chapitre while...end ce qu'il faut entendre par expression_logique.

Ex:

  • Soit le bloc d'instructions if n==1, disp('un'), elseif n==2, disp('deux'), else, disp('autre'), end. Si l'on affecte n=1, l'exécution de ces instructions affiche "un", si l'on affecte n=2 il affiche "deux", et si "n" est autre que 1 ou 2 il affiche "autre"
Construction switch...case...otherwise...end

switch expression
   case val1
      instructions_a...
   case {val2, val3 ...}
      instructions_b...
 { otherwise
      autres_instructions... }
end
Cette structure de contrôle réalise ce qui suit : si l'expression spécifiée (qui peut se résumer à une variable) retourne la valeur val1, seul le bloc d'instructions_a qui suit est exécuté. Si elle retourne la valeur val2 ou val3, seul le bloc de instructions_b correspondant est exécuté... et ainsi de suite. Si elle ne retourne rien de tout ça et qu'on a défini une clause otherwise, c'est le bloc des autres_instructions correspondant qui est exécuté.

Important : notez bien que si, dans une clause case, vous définissez plusieurs valeurs val possibles, il faut que celles-ci soient définies sous la forme de tableau cellulaire, c'est-à-dire entre caractères { }.

Contrairement au "switch" du langage C, il n'est pas nécessaire de définir des break à la fin de chaque bloc.
On peut avoir autant de blocs case que l'on veut, et la partie otherwise est donc facultative.
En lieu et place de val1, val2... on peut bien entendu aussi mettre des expressions.

Ex:

  • Tester d'une réponse rep contenant : Non, non, No, no, N, n, Oui, oui, O, o, Yes, yes, Y, y :
    switch lower(rep(1))
        case 'n'
            disp('non')
        case { 'o' 'y' }
            disp('oui')
        otherwise
            disp('reponse incorrecte')
    end
  • L'exemple précédent réalisé avec if-elseif-else pourrait être ainsi reprogrammé avec une structure switch-case : switch n, case 1, disp('un'), case 2, disp('deux'), otherwise, disp('autre'), end
Construction try...catch...end

try
   instructions_1...
catch
   instructions_2...
end
autres_instructions...
Cette construction sert à implémenter des traitements d'erreur.

Les instructions comprises entre try et catch (bloc instructions_1) sont exécutées jusqu'à ce qu'une erreur se produise, auquel cas MATLAB/Octave passe automatiquement à l'exécution des instructions comprises entre catch et end (bloc instructions_2). Le message d'erreur ne s'affiche pas mais peut être récupéré avec la commande lasterr.

Si le premier bloc de instructions_1 s'exécute absolument sans erreur, le second bloc de instructions_2 n'est pas exécuté, et le déroulement se poursuit avec autres_instructions

Sortie anticipée d'une boucle

break

A l'intérieur d'une boucle for, while ou do, cette instruction permet, par exemple suite à un test, de sortir prématurément de la boucle et de poursuivre l'exécution des instructions situées après la boucle. Si 2 boucles sont imbriquées l'une dans l'autre, un break placé dans la boucle interne sort de celle-ci et continue l'exécution dans la boucle externe.
A ne pas confondre avec return (voir plus bas) qui sort d'une fonction, respectivement interrompt un script !

Ex: sortie d'une boucle for :

for k=1:100
  k2=k^2;
  fprintf('carré de %3d = %5d \n',k,k2)
  if k2 > 200 break, end  % sortir boucle lorsque k^2 > 200
end
fprintf('\nsorti de la boucle à k = %d\n',k)

Ex: sortie d'une boucle while :

k=1;
while true    % à priori boucle sans fin !
  fprintf('carré de %3d = %5d \n', k, k^2)
  if k >= 10 break, else k=k+1; end
  % ici on sort lorsque k > 10
end
Sauter le reste des instructions d'une boucle et continuer d'itérer

continue

A l'intérieur d'une boucle (for ou while), cette instruction permet donc, par exemple suite à un test, de sauter le reste des instructions de la boucle et passer à l'itération suivante de la même boucle.

Ex:

start=1; stop=100; fact=8;
fprintf('Nb entre %u et %u divisibles par %u : ',start,stop,fact)

for k=start:1:stop
  if rem(k,fact) ~= 0
    continue
  end
  fprintf('%u, ', k)
end

disp('fin')
Le code ci-dessus affiche: "Nb entre 1 et 100 divisibles par 8 : 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, fin"
double(var) Cette instruction permet de convertir en double précision la variable var qui, dans certaines constructions for, while, if, peut n'être qu'en simple précision



Les structures de contrôle sont donc des éléments de langage extrêmement utiles. Mais dans MATLAB/Octave, il faut "penser instructions matricielles" (on dit aussi parfois "vectoriser" son algorithme) avant d'utiliser à toutes les sauces ces structures de contrôle qui, du fait que MATLAB est un langage interprété, sont beaucoup moins rapides que les opérateurs et fonctions matriciels de base !

Ex: l'instruction y=sqrt(1:100000); est beaucoup plus efficace/rapide que la boucle for n=1:100000, y(n)=sqrt(n); end (bien que, dans les 2 cas, ce soit un vecteur de 100'000 éléments qui est créé contenant les valeurs de la racine de 1 jusqu'à la racine de 100'000). Testez vous-même !

7.5 Scripts (programmes), mode batch

Scripts


19:16 min
Les "scripts" sont la dénomination des programmes sous MATLAB/Octave. Dans cette vidéo nous voyons :
• ce qui caractérise un script
• comment un script peut interagir avec l'extérieur, que ce soit à travers le workspace, ou interactivement avec l'utilisateur
• comment on peut documenter un script ainsi qu'élaborer une aide en-ligne facilitant son utilisation
• comment utiliser efficacement l'éditeur intégré des environnements de développement Octave et MATLAB
• ce qu'est le "prologue utilisateur" sous MATLAB/Octave

7.5.1 Principes de base relatifs aux scripts

Un "script" ou "programme" MATLAB/Octave n'est rien d'autre qu'une suite de commandes MATLAB/Octave valides (par exemple un "algorithme" exprimé en langage MATLAB/Octave) sauvegardées dans un M-file, c'est-à-dire un fichier avec l'extension .m.

Par opposition aux "fonctions" (voir chapitre suivant), les scripts sont invoqués par l'utilisateur sans passer d'arguments, car ils opèrent directement dans le workspace principal. Un script peut donc lire et modifier des variables préalablement définies (que ce soit interactivement ou via un autre script), ainsi que créer de nouvelles variables qui seront accessibles dans le workspace (et à d'autres scripts) une fois le script exécuté.

À partir de la version R2016B de MATLAB, il est possible de définir des fonctions à l'intérieur d'un script (ce qui n'était auparavant pas supporté sous MATLAB). Il faut cependant prendre garde au fait que :

Il est possible (et vivement conseillé) de documenter le fonctionnement du script vis-à-vis du système d'aide en ligne help de MATLAB/Octave. Il suffit, pour cela, de définir, au tout début du script, des lignes de commentaire (lignes débutant par le caractère %). La commande help M-file affichera alors automatiquement le 1er bloc de lignes de commentaire contiguës du M-file. On veillera à ce que la toute première ligne de commentaire (appelée "H1-line") indique le nom du script (en majuscules) et précise brièvement ce que fait le script, étant donné que c'est cette ligne qui est affichée lorsque l'on fait une recherche de type lookfor mot-clé.

Pour exécuter un script, on peut utiliser l'une des méthodes suivantes, selon que l'on soit dans l'éditeur intégré, dans la console MATALAB/Octave ou depuis un autre script :

a) Run, Save File and Run ou F5
b) script enter
c) run('{chemin/}script{.m}') ou run {chemin/}script{.m}
d) source('{chemin/}script.m') ou source {chemin/}script.m

a) Lancement de l'exécution depuis l'éditeur intégré MATLAB ou Octave GUI
b) Le script doit obligatoirement se trouver dans le répertoire courant ou dans le path de recherche MATLAB/Octave (voir chapitre "Environnement"). Il ne faut pas spécifier l'extension .m du script
c) Cette forme permet d'exécuter un script situé en dehors du répertoire courant en indiquant le chemin d'accès (absolu ou relatif). On peut omettre ou spécifier l'extension .m du script
d) Cette forme est propre à Octave. Dans ce cas l'extension .m doit obligatoirement être indiquée

En phase de debugging, on peut activer l'affichage des commandes exécutées par le script en passant la commande echo on avant de lancer le script, puis désactiver ce "traçage" avec echo off une fois le script terminé.


Exemple de script: Le petit programme ci-dessous réalise la somme et le produit de 2 nombres, vecteurs ou matrices (de même dimension) demandés interactivement. Notez bien la 1ère ligne de commentaire (H1-line) et les 2 lignes qui suivent fournissant le texte pour l'aide en-ligne. On exécute ce programme en frappant somprod (puis répondre aux questions interactives...), ou l'on obtient de l'aide sur ce script en frappant help somprod.

%SOMPROD Script réalisant la somme et le produit de 2 nombres, vecteurs ou matrices
%
% Ce script est interactif, c'est-à-dire qu'il demande interactivement les 2 nombres,
% vecteurs ou matrices dont il faut faire la somme et le produit (élément par élément)

V1=input('Entrer 1er nombre (ou expression, vecteur ou matrice) : ') ;
V2=input('Entrer 2e  nombre (ou expression, vecteur ou matrice) : ') ;

if ~ isequal(size(V1),size(V2))
  error('les 2 arguments n''ont pas la meme dimension')
end

%{
  1ère façon d'afficher les résultats (la plus propre au niveau affichage,
  mais ne convenant que si V1 et V2 sont des scalaires) :
      fprintf('Somme = %6.1f  Produit = %6.1f \n', V1+V2, V1.*V2)

  2ème façon d'afficher les résultats :
      Somme = V1+V2
      Produit = V1.*V2
%}

% 3ème façon (basique) d'afficher les résultats
     disp('Somme =') ,   disp(V1+V2)
     disp('Produit =') , disp(V1.*V2)

return  % Sortie du script (instruction ici pas vraiment nécessaire,
        %                   vu qu'on a atteint la fin du script !)

7.5.2 Exécuter un script en mode batch

Pour autant qu'il ne soit pas interactif, on peut exécuter un script depuis un shell (dans fenêtre de commande du système d'exploitation) ou en mode batch (p.ex. environnement GRID), c'est-à-dire sans devoir démarrer l'interface-utilisateur MATLAB/Octave, de la façon décrite ici.

Avec MATLAB :


Avec Octave :


En outre avec Octave, si vous ne désirez exécuter en "batch" que quelques commandes sans faire de script, vous pouvez procéder ainsi :


7.5.3 Tester si un script s'exécute sous MATLAB ou sous Octave

Étant donné les différences qui peuvent exister entre MATLAB et Octave (p.ex. fonctions implémentées différemment ou non disponibles...), si l'on souhaite réaliser des scripts portables (i.e. qui tournent à la fois sous MATLAB et Octave, ce qui est conseillé !) on peut implémenter du code conditionnel relatif à chacun de ces environnements en réalisant, par exemple, un test via une fonction built-in appropriée.

Ex: on test ici l'existence de la fonction built-in OCTAVE_VERSION (n'existant que sous Octave) :

  if exist('OCTAVE_VERSION')  % Octave
    % ici instruction(s) pour Octave
  else                        % MATLAB
    % ici instruction(s) équivalente(s) pour MATLAB
  end

7.6 Fonctions

Fonctions


17:15 min
Les fonctions utilisateur sont fondamentales en programmation, permettant notamment de rendre un code plus modulaire, plus lisible et moins redondant. Nous montrons dans cette vidéo :
• ce qui distingue fondamentalement les fionctions des scripts (présentés dans une vidéo précédente), au niveau de leur implémentation et de leur utilisation, notamment la question du passage d'arguments en entrée et la récupération de données en sortie
• la portée et la durée de vie des variables au sein des fonctions, et comment modifier cela (avec les instructions global et persistent)
• ce qu'il est possible de faire, s'agissant de la combinaison, dans un même M-file, de plusieurs fonctions

7.6.1 Principes de base relatifs aux fonctions

Également programmées sous la forme de M-files, les "fonctions" MATLAB/Octave se distinguent des "scripts" par la manière de les utiliser. On les appelle ainsi :
        [variable(s) de sortie ...] = nom_fonction(paramètre(s) d'entree ...)

Le mécanisme de passage des paramètres à la fonction s'effectue "par valeur" (c'est-à-dire copie) et non pas "par référence". La fonction ne peut donc pas modifier les variables d'entrée au niveau du script appelant (workspace principal) ou de la fonction appelante (workspace de cette dernière).

Les variables créées à l'intérieur de la fonction sont dites "locales", c'est-à-dire qu'elles sont par défaut inaccessibles en dehors de la fonction (que ce soit dans le workspace principal ou dans d'autres fonctions ou scripts). Chaque fonction dispose donc de son propre workspace local de fonction.

Il est possible de définir, dans le même M-file, plusieurs fonctions à la suite les unes des autres. Cependant seule la première fonction du M-file, appelée fonction principale (main function), sera accessible de l'extérieur. Les autres, appelées fonctions locales (ou subfunctions), ne pourront être appelées que par la fonction principale ou les autres fonctions locales du M-file.

Il est finalement possible de définir des fonctions à l'intérieur du corps d'une fonction. Ces fonctions imbriquées (nested function) ne pourront cependant être invoquées que depuis la fonction dans laquelle elles sont définies.


Le code de toute fonction débute par une ligne de déclaration de fonction qui définit son nom_fonction et ses arguments args_entree et args_sortie (séparés par des virgules), selon la syntaxe :

      function [argument(s)_de_sortie, ...] = nom_fonction(argument(s)_d_entree, ...)

Octave affiche un warning si le nom de la fonction principale (tel que défini dans la 1ère déclaration de fonction) est différent du nom du M-file. Sous MATLAB, le fait que les 2 noms soient identiques n'est pas obligatoire mais fait partie des règles de bonne pratique.

Se succèdent donc, dans le code d'une fonction (et dans cet ordre) :

  1. déclaration de la fonction principale (ligne function... décrite ci-dessus)
  2. lignes de commentaires (commençant par le caractère %) décrivant la fonction pour le système d'aide en-ligne, à savoir :
    • la "H1-line" précisant le nom de la fonction et indiquant des mots-clé (ligne qui sera retournée par la commande lookfor mot-clé)
    • les lignes du texte d'aide (qui seront affichées par la commande help nom_fonction)
  3. déclarations d'éventuelles variables globale ou statique
  4. code proprement dit de la fonction (qui va affecter les variables argument(s)_de_sortie)
  5. éventuelle(s) instruction(s) return définissant de(s) point(s) de sortie de la fonction
  6. instruction end signalant la fin de la fonction
Les templates de fonctions Octave se terminent par l'instruction endfunction, mais nous vous suggérons de la remplacer par end afin que vos fonctions soient compatibles à la fois pour MATLAB et Octave. Mais en fait sous MATLAB et Octave, l'instruction end finale d'une fonction peut aussi être omise, sauf lorsque l'on définit plusieurs fonctions dans un même M-file.


Exemple de fonction: On présente, ci-dessous, deux façons de réaliser une petite fonction retournant le produit et la somme de 2 nombres, vecteurs ou matrices. Dans les deux cas, le M-file doit être nommé fsomprod.m (c'est-à-dire identique au nom de la fonction). On peut accéder à l'aide de la fonction avec help fsomprod, et on affiche la première ligne d'aide en effectuant par exemple une recherche lookfor produit. Dans ces 2 exemples, mis à part les arrêts en cas d'erreurs (instructions error), la sortie s'effectue à la fin du code mais aurait pu intervenir ailleurs (instructions return) !

Fonction Appel de la fonction
function resultat = fsomprod(a,b)
%FSOMPROD somme et produit de 2 nombres ou vecteurs-ligne
%   Usage: R=FSOMPROD(V1,V2)
%      Retourne matrice R contenant: en 1ère ligne la
%      somme de V1 et V2, en seconde ligne le produit de
%      V1 et V2 élément par élément

  if nargin~=2
    error('cette fonction attend 2 arguments')
  end
  
  sa=size(a); sb=size(b);
  if ~ isequal(sa,sb)
    error('les 2 arguments n'ont pas la même dimension')
  end
  if sa(1)~=1 || sb(1)~=1
    error('les arg. doivent être scalaires ou vecteurs-ligne')
  end
  
  resultat(1,:)=a+b;  % 1ère ligne de la matrice-résultat
  resultat(2,:)=a.*b; % 2ème ligne de la matrice-résultat,
                      %      produit élément par élément !
  return     % sortie de la fonction (instruction ici pas
             % nécessaire vu qu'on a atteint fin fonction)
end  % pas nécessaire si le fichier ne contient que cette fct
Remarque : cette façon de retourner le résultat (sur une seule variable) ne permet pas de passer à cette fonction des matrices.

r=fsomprod(4,5)
retourne la vecteur-colonne r=[9 ; 20]

r=fsomprod([2 3 1],[1 2 2])
retourne la matrice r=[3 5 3 ; 2 6 2]

function [somme,produit] = fsomprod(a,b)
%FSOMPROD somme et produit de 2 nombres, vecteurs ou matrices
%   Usage: [S,P]=FSOMPROD(V1,V2)
%      Retourne matrice S contenant la somme de V1 et V2,
%      et matrice P contenant le produit de V1 et V2
%      élément par élément
  
  if nargin~=2
    error('cette fonction attend 2 arguments')
  end
  if ~ isequal(size(a),size(b))
    error('les 2 arg. n'ont pas la même dimension')
  end
  
  somme=a+b;
  produit=a.*b; % produit élément par élément !
  return        % sortie de la fonction (instruction ici pas
                % nécessaire vu qu'on a atteint fin fonction)
end  % pas nécessaire si le fichier ne contient que cette fct
Remarque : cette façon de retourner le résultat (sur deux variable) rend possible le passage de matrices à cette fonction !

[s,p]=fsomprod(4,5)
retourne les scalaires s=9 et p=20

[s,p]=fsomprod([2 3;1 2],[1 2; 3 3])
retourne les matrices s=[3 5 ; 4 5] et p=[2 6 ; 3 6]

7.6.2 Pointeurs de fonctions et fonctions anonymes

Un pointeur de fonction (function handle) est une technique alternative d'invocation d'une fonction. L'intérêt réside dans le passage de fonctions à d'autres fonctions et la définition de fonctions anonymes (voir ci-dessous). Les pointeurs de fonctions sont notamment beaucoup utilisés pour définir les callbacks dans les interfaces utilisateurs graphiques (voir chapitre "Programmation GUI").
Ex:
  • si l'on défini f = @sin ;, on peut tracer le graphique de cette fonction avec fplot(f, [0 2*pi]) (qui est équivalent à fplot(@sin, [0 2*pi]))
  • on peut bien entendu ensuite calculer le sinus de π avec f(pi) qui est équivalent à feval(f, pi)
  • Une fonction anonyme (anonymous function) est une façon très concise de définir une fonction-utilisateur basée sur une expression.
    Ex:
  • on pourrait définir la fonction "carré" avec carre = @(nb) nb.^2 ; puis l'invoquer avec carre([2 3 4]) qui retournera [4 9 16]
      ou la grapher avec fplot(carre, [0 3]), ou tracer la fonction carré de façon complètement anonyme avec fplot(@(nb) nb.^2, [0 3])
  • la fonction somme = @(nb1, nb2) nb1+nb2 ; implémente la somme de 2 variables, et somme([2 3], [4 1]) retournera donc [6 4]


  • Une fonction inline est une ancienne manière de définir une fonction anonyme. Notez cependant que les fonctions inline vont disparaître dans une prochaine version de MATLAB et que Octave suivra également le mouvement... donc ne les utilisez en principe plus.
    Ex:
  • on pourrait définir la fonction "carré" avec carre = inline('nb.^2') ; puis l'invoquer avec carre([2 3 4]) qui retournera [4 9 16]
  • la fonction somme = inline('nb1+nb2', 'nb1', 'nb2') implémente la somme de 2 variables, et somme([2 3], [4 1]) retournera donc [6 4]
  • 7.6.3 P-Code

    Lorsque du code MATLAB est exécuté, il est automatiquement interprété et traduit ("parsing") dans un langage de plus bas niveau qui s'appelle le P-Code (pseudo-code). Sous MATLAB seulement, s'agissant d'une fonction souvent utilisée, on peut éviter que cette "passe de traduction" soit effectuée lors de chaque appel en sauvegardant le P-Code sur un fichier avec la commande pcode nom_fonction. Un fichier de nom nom_fonction.p est alors déposé dans le répertoire courant (ou dans le dossier où se trouve le M-file si l'on ajoute à la commande pcode le paramètre -inplace), et à chaque appel la fonction pourra être directement exécutée sur la base du P-Code de ce fichier sans traduction préalable, ce qui peut apporter des gains de performance.

    Le mécanisme de conversion d'une fonction ou d'un script en P-Code offre également la possibilité de distribuer ceux-ci à d'autres personnes sous forme binaire en conservant la maîtrise du code source.

    7.7 Autres commandes et fonctions utiles en programmation

    Nous énumérons encore ici quelques commandes ou fonctions supplémentaires qui peuvent être utiles dans la programmation de scripts ou de fonctions.

    return
    Termine l'exécution de la fonction ou du script. Un script ou une fonction peut renfermer plusieurs return (sorties contrôlées par des structures de contrôle...). Une autre façon de sortir proprement en cas d'erreur est d'utiliser la fonction error (voir plus haut).
    On ne sortira jamais avec exit ou quit qui non seulement terminerait le script ou la fonction mais fermerait aussi la session MATLAB/Octave !
    var= nargin
    A l'intérieur d'une fonction, retourne le nombre d'arguments d'entrée passés lors de l'appel à cette fonction. Permet par exemple de donner des valeurs par défaut aux paramètres d'entrée manquant.
    Utile sous Octave pour tester si le nombre de paramètres passés par l'utilisateur à la fonction est bien celui attendu par la fonction (ce test n'étant pas nécessaire sous MATLAB ou le non respect de cette condition est automatiquement détecté).
    Voir aussi la fonction nargchk qui permet aussi l'implémentation simple d'un message d'erreur.

    Ex: voir ci-après

    varargin
    A l'intérieur d'une fonction, tableau cellulaire permettant de récupérer un nombre d'arguments quelconque passé à la fonction

    Ex: soit la fonction test_vararg.m suivante :

    function test_vararg(varargin)
      fprintf('Nombre d''arguments passes a la fonction : %d \n',nargin)
      for no_argin=1:nargin
        fprintf('- argument %d:\n', no_argin)
        disp(varargin{no_argin} )
      end
    end
            si on l'invoque avec test_vararg(111,[22 33;44 55],'hello !',{'ca va ?'}) elle retourne :
    Nombre d'arguments passes a la fonction : 4 
    - argument 1:
       111
    - argument 2:
       22   33
       44   55
    - argument 3:
       hello !
    - argument 4:
       { [1,1] = ca va ? }
    string= inputname(k)
    A l'intérieur d'une fonction, retourne le nom de variable du k-ème argument passé à la fonction
    var= nargout
    A l'intérieur d'une fonction, retourne le nombre de variables de sortie auxquelles la fonction est affectée lors de l'appel. Permet par exemple d'éviter de calculer les paramètres de sortie manquants....
    Voir aussi la fonction nargoutchk qui permet aussi l'implémentation simple d'un message d'erreur.

    Ex: A l'intérieur d'une fonction-utilisateur mafonction :
      • lorsqu'on l'appelle avec mafonction(...) : nargout vaudra 0
      • lorsqu'on l'appelle avec out1 = mafonction(...) : nargout vaudra 1
      • lorsqu'on l'appelle avec [out1 out2] = mafonction(...) : nargout vaudra 2, etc...

    string= mfilename
    A l'intérieur d'une fonction ou d'un script, retourne le nom du M-file de cette fonction ou script, sans son extension .m

    Ex d'instruction dans une fonction : warning(['La fonction ' mfilename ' attend au moins un argument'])

    global variable(s)
    Définit la(les) variable(s) spécifiée(s) comme globale(s). Cela peut être utile lorsque l'on veut partager des données entre fonctions sans devoir passer ces données en paramètre lors de l'appel à ces fonctions. Il est alors nécessaire de déclarer ces variables globales, avant de les utiliser, que ce soit interactivement sans la console ou dans des scripts ou fonctions.
    Une bonne habitude serait d'identifier clairement les variables globales de fonctions, par exemple en leur donnant un nom en caractères majuscules.

    Ex : la fonction fct1.m ci-dessous mémorise (et affiche) le nombre de fois qu'elle a été appelée :

    function fct1
      global COMPTEUR
      COMPTEUR=COMPTEUR+1;
      fprintf('fonction appelee %04u fois \n',COMPTEUR)
    end
    Pour tester cela, il faut passer les instructions suivantes dans la fenêtre de commande MATLAB/Octave :
    global COMPTEUR   % cela déclare le compteur également global dans le workspace
    COMPTEUR = 0 ;    % initialisation du compteur
    fct1              % => cela affiche "fonction appelee   1 fois"
    fct1              % => cela affiche "fonction appelee   2 fois"
    
    persistent variable(s)
    Utilisable dans les fonctions seulement, cette déclaration définit la(les) variable(s) spécifiée(s) comme statique(s), c'est-à-dire conservant de façon interne leurs dernières valeurs entre chaque appel à la fonction. Ces variables ne sont cependant pas visibles en-dehors de la fonction (par opposition aux variables globales).

    Ex : la fonction fct2.m ci-dessous mémorise (et affiche) le nombre de fois qu'elle a été appelée. Contrairement à l'exemple de la fonction fct1.m ci-dessus, la variable compteur n'a pas à être déclarée dans la session principale (ou dans le script depuis lequel on appelle cette fonction), et le compteur doit ici être initialisé dans la fonction.

    function fct2
      persistent compteur
      % au premier appel, après cette déclaration persistent compteur existe et vaut []
      if isempty(compteur)
        compteur=0 ;
      end
      compteur=compteur+1 ;
      fprintf('fonction appelee %04u fois \n',compteur)
    end
    Pour tester cela, il suffit de passer les instructions suivantes dans la fenêtre de commande MATLAB/Octave :
    fct2              % => cela affiche "fonction appelee   1 fois"
    fct2              % => cela affiche "fonction appelee   2 fois"
    
    a) eval('expression1', {'expression2'})
    b) var = evalc(...)
    a) Évalue et exécute l'expression1 MATLAB/Octave spécifiée. En cas d'échec, évalue l'expression2
    b) Comme a) sauf que l'output et les éventuelles erreurs sont envoyés sur var

    Ex: le petit script suivant permet de grapher n'importe quelle fonction y=f(x) définie interactivement par l'utilisateur :

    fonction = input('Quelle fonction y=fct(x) voulez-vous grapher : ','s');
      min_max  = input('Indiquez [xmin xmax] : ');
      x = linspace(min_max(1),min_max(2),100);
      eval(fonction,'error(''fonction incorrecte'')');
      plot(x,y)
    end
    class(objet)
    Retourne la "classe" de objet (double, struct, cell, char).
    typeinfo(objet)
    Sous Octave seulement, retourne le "type" de objet (scalar, range, matrix, struct, cell, list, bool, sq_string, char matrix, file...).

    7.8 Entrées-sorties formatées, manipulation de fichiers

    Lorsqu'il s'agit de charger, dans MATLAB/Octave, une matrice à partir de données externes stockées dans un fichier-texte, les commandes load -ascii et dlmread/dlmwrite, présentées au chapitre "Workspace MATLAB/Octave", sont suffisantes. Mais lorsque les données à importer sont dans un format plus complexe ou qu'il s'agit d'importer du texte ou d'exporter des données vers d'autres logiciels, les fonctions présentées ci-dessous s'avèrent nécessaires.

    7.8.1 Vue d'ensemble des fonctions d'entrée/sortie de base

    Le tableau ci-dessous donne une vision synthétique des principales fonctions d'entrée/sortie (présentées en détail dans les chapitres qui suivent). Notez que :

      Écriture Lecture
    Interactivement Écriture à l'écran (sortie standard)
    • non formaté: disp(variable|chaîne)
      (avec un seul paramètre !)
    • formaté: fprintf(format, variable(s))
      (ou printf...)
    Lecture au clavier (entrée standard)
    • non formaté:   var = input(prompt {, 's'} )

    • formaté:   var = scanf(format)
    Sur chaîne de caractères string = sprintf(format, variable(s))

    • autres fonctions : mat2str ...
    var|mat = sscanf(string, format {,size})
    • [var1, var2... ] = strread(string,format {,n})

    • autres fonctions : textscan ...
    Sur fichier texte fprintf(file_id, format, variable(s)... )

    • autres fonctions : save -ascii, dlmwrite ...
    var = fscanf(file_id, format {,size})
    line = fgetl(file_id)
    string = fgets(file_id {,nb_car})

    • autres fonctions : load(fichier), textread, textscan, fileread, dlmread ...
    Via internet
    (protocoles HTTTP, FTP ou FILE)
    string = urlread(url, method, param)
    urlwrite(url, fichier, method, param)
      (pour url de type HTTP, method : 'get' ou 'post')
    • autres fonctions : webread et webwrite (RESTful web services )
    string = urlread(url)
    urlwrite(url, fichier)

    • autres fonctions : webread et webwrite (RESTful web services )
    Sur fichier binaire • autres fonctions : fwrite, xlswrite ... • autres fonctions : fread, xlsread ...

    7.8.2 Formats de lecture/écriture

    Formats, écriture et décodage de chaînes


    08:10 min
    Les spécifications de format et leur usage dans l'écriture et décodage de chaînes

    Les différentes fonctions de lecture/écriture sous forme texte présentées ci-dessous font appel à des "formats" (parfois appelés "templates" dans la documentation). Le but de ceux-ci est de :

    Les formats MATLAB/Octave utilisent un sous-ensemble des conventions et spécifications de formats du langage C.

    Les formats sont des chaînes de caractères se composant de "spécifications de conversion" dont la syntaxe est décrite dans le tableau ci-dessous.

    ATTENTION : dans un format de lecture (sscanf, fscanf), on ne préfixera en principe pas les "spécifications de conversion" de nombres (u d i o x X f e E g G) par des valeurs n (taille du champ) et m (nombre de décimales), car le comportement de MATLAB et de Octave peut alors conduire à des résultats différents (découpage avec MATLAB, et aucun effet sous Octave).

          Ex: sscanf('560001','%4f') retourne : sous MATLAB le vecteur [5600 ; 1] , et sous Octave la valeur 560001

    Spécifications Description
    espace • En lecture (sscanf, fscanf) : les caractères espace dans un format sont ignorés (i.e. n'ont aucune signification) !
    • En écriture (sprintf, fprintf) : les caractères espace dans un format sont écrits dans la chaîne résultante !
    %u

      %nu

    Correspond à un nombre entier positif (non signé)

    • En lecture:
          Ex: sscanf('100 -5','%u') => 100 et 4.295e+09 (!)
    • En écriture: si n est spécifié, le nombre sera justifié à droite dans un champ de n car. au min.
          Ex: sprintf('%5u ', 100, -5) => '  100 -5.000000e+000' et '  100     -5'

    %d   %i

    %nd   %ni

    Correspond à un nombre entier positif ou négatif

    • En lecture:
          Ex: sscanf('100 -5','%d') => 100 et -5
    • En écriture: si n est spécifié, le nombre sera justifié à droite dans un champ de n car. au min.
          Ex: sprintf('%d %03d ', 100, -5) => '100 -05'

    %o

      %no

    Correspond à un nombre entier positif en base octale

    • En lecture:
          Ex: sscanf('100 -5','%o') => 64 et 4.295e+09 (!)
    • En écriture: si n est spécifié, le nombre sera justifié à droite dans un champ de n car. au min.
          Ex: sprintf('%04o ', 64, -5) => '0100 -5.000000e+000' et '0100 -005'

    %x   %X

      %nx   %nX

    Correspond à un nombre entier positif en base hexadécimale

    • En lecture:
          Ex: sscanf('100 -5','%x') => 256 et 4.295e+09 (!)
    • En écriture: si n est spécifié, le nombre sera justifié à droite dans un champ de n car. au min.
          Ex: sprintf('%x %04X ', 255, 255, -5) => 'ff 00FF -5.000000e+000' et 'ff 00FF -5'

    %f

    %nf   %n.mf

    Correspond à un nombre réel sans exposant (de la forme {-}mmm.nnn)

    • En lecture:
          Ex: sscanf('5.6e3 xy -5','%f xy %f') => [5600 ; -5]
    • En écriture: si n est spécifié, le nombre sera justifié à droite dans un champ de n car. au min., et affiché avec m chiffres après la virgule.
          Ex1: sprintf('%4.2f', 3.467) => '3.47' donc notez bien qu'il y a un arrondi automatique et non pas une troncature (i.e. on ne reçoit pas '3.46')
          Ex2: sprintf('%f %0.2f ', 56e002, -78e-13, -5) => '5600.000000 -0.00 -5.000000'

    %e   %E

    %ne   %nE

      %n.me   %n.mE

    Correspond à un nombre réel en notation scientifique (de la forme {-}m.nnnnnE{+|-}xxx)

    • En lecture:
          Ex: sscanf('5.6e3 xy -5','%e %*s %e') => [5600 ; -5]
    • En écriture: si n est spécifié, le nombre sera justifié à droite dans un champ de n car. au min., et affiché avec m chiffres après la virgule. Avec e, le caractère 'e' de l'exposant sera en minuscule ; avec E il sera en majuscule.
          Ex: sprintf('%e %0.2E ', 56e002, -78e-13, -5) => '5.600000e+003   -7.80E-12   -5.000000e+00'

    %g   %G

      %ng   %nG

    Correspond à un nombre réel en notation scientifique compacte (de la forme {-}m.nnnnnE{+|-}x)

    • En lecture:
          Ex: sscanf('5.6e3 xy -5','%g %*2c %g') => [5600 ; -5]
    • En écriture: donne lieu à une forme plus compacte que %f et %e. Si n est spécifié, le nombre sera justifié à droite dans un champ de n car. au min. Avec g, le caractère 'e' de l'exposant sera en minuscule ; avec G il sera en majuscule.
          Ex: sprintf('%g %G ', 56e002, -78e-13, -5) => '5600   -7.8E-12   -5'

    %c

      %nc

    Correspond à 1 ou n caractère(s), y compris d'éventuels caractères espace

    • En lecture:
          Ex: sscanf('ab1 2cd3 4ef', '%2c %*3c') => 'abcdef'
    • En écriture:
          Ex: sprintf(' %c: (ASCII: %3d)\n', 'aabbcc') => cela affiche :
                a: (ASCII: 97)
                b: (ASCII: 98)
                c: (ASCII: 99)

    %s

      %ns

    Correspond à une chaîne de caractères

    • En lecture: les chaînes sont délimitées par un ou plusieurs caractères espace
          Ex: sscanf('123   abcd', '%2s %3s') => '123abcd'
    • En écriture: si n est spécifié, la chaîne sera justifiée à droite dans un champ de n car. au min.
          Ex: sprintf('%s|%5s|%-5s|', 'blahblah...', 'abc', 'XYZ')
          => 'blahblah...|   abc|XYZ   |'

    Caractères
        spéciaux
    Pour faire usage de certains caractères spéciaux, il est nécessaire de les encoder de la façon suivante :
    \n pour un saut à la ligne suivante (new line)
    \t pour un tab horizontal
    %% pour le caractère "%"
    \\ pour le caractère "\"

      Tout autre
      caractère
    Tout autre caractère (ne faisant pas partie d'une spécification %...) sera utilisé de la façon suivante :

    • En lecture: le caractère "matché" sera sauté. Exception: les caractères espace dans un format de lecture sont ignorés
          Ex: sscanf('12 xy 34.5 ab 67', '%f xy %f ab %f') => [12.0 ; 34.5 ; 67.0]
    • En écriture: le caractère sera écrit tel quel dans la chaîne de sortie
          Ex: article='stylos' ; fprintf('Total: %d %s \n', 4, article) => 'Total: 4 stylos'


    De plus, les "spécifications de conversion" peuvent être modifiées (préfixées) de la façon suivante :

    Spécifications Description
    %-n... • En écriture: l'élément sera justifié à gauche (et non à droite) dans un champ de n car. au min.

          Ex: sprintf('|%-5s|%-5.1f|', 'abc', 12) => '|abc  |12.0 |'

    %0n... • En écriture: l'élément sera complété à gauche par des '0' (chiffres zéros, et non caractères espace) dans un champ de n car. au min.

          Ex: sprintf('|%05s|%05.1f|', 'abc', 12) => '|00abc|012.0|'

    %*... • En lecture: saute l'élément qui correspond à la spécification qui suit

          Ex: sscanf('12 blabla 34.5 67.8', '%d %*s %*f %f') => [12 ; 67.8]

    7.8.3 Lecture/écriture formatée de chaînes

    Lecture/décodage de chaîne

    La fonction sscanf ("string scan formated") permet, à l'aide d'un format de lecture, de décoder le contenu d'une chaîne de caractère et d'en récupérer les données sur un vecteur ou une matrice. La lecture s'effectue en "format libre" en ce sens que sont considérés, comme séparateurs d'éléments dans la chaîne, un ou plusieurs espace ou tab. Si la chaîne renferme davantage d'éléments qu'il n'y a de "spécifications de conversion" dans le format, le format sera "réutilisé" autant de fois que nécessaire pour lire toute la chaîne. Si, dans le format, on mélange des spécifications de conversion numériques et de caractères, il en résulte une variable de sortie (vecteur ou matrice) entièrement numérique dans laquelle les caractères des chaînes d'entrée sont stockés, à raison d'un caractère par élément de vecteur/matrice, sous forme de leur code ASCII.

    vec = sscanf(string, format)
    [vec, count] = sscanf(string, format)
    Décode la chaîne string à l'aide du format spécifié, et retourne le vecteur-colonne vec dont tous les éléments seront de même type. La seconde forme retourne en outre, sur count, le nombre d'éléments générés.

    Ex:
    vec=sscanf('abc 1 2 3 4 5 6', '%*s %f %f') => vec=[1;2;4;5]
        Notez que, en raison de la "réutilisation" du format, les nombres 3 et 6 sont ici sautés par le %*s !
    vec=sscanf('1001 1002 abc', '%f %f %s') => vec=[1001;1002;87;98;99]
        Mélange de spécifications de conversion numériques et de caractères => la variable 'vec' est de type nombre, et la chaîne 'abc' y est stockée par le code ASCII de chacun de ses caractères

    mat = sscanf(string, format, size)
    [mat, count] = sscanf(string, format, size)
    Permet de remplir une matrice mat, colonne après colonne. La syntaxe du paramètre size est :
    nb => provoque la lecture des nb premiers éléments, et retourne un vecteur colonne
    [nb_row, nb_col] => lecture de nb_row x nb_col éléments, et retourne une matrice de dimension nb_row x nb_col

    Ex:
    vec=sscanf('1 2 3 4 5 6', '%f', 4) => vec=[1;2;3;4]
    [mat,ct]=sscanf('1 2 3 4 5 6', '%f', [3,2]) => mat=[1 4 ; 2 5 ; 3 6], ct=6
    [mat,ct]=sscanf('1 2 3 4 5 6', '%f', [2,3]) => mat=[1 3 5 ; 2 4 6], ct=6

    [var1, var2, var3 ...] = sscanf(string, format, 'C')
    (Proche du langage C, cette forme très flexible n'est disponible que sous Octave)
    Chaque "spécification de conversion" définie dans le format est associée à une variable de sortie var-i qui sera du type correspondant !

    Ex: • [str,nb1,nb2]=sscanf('abcde 12.34 45.3e14 fgh', '%3c %*s %f %f', 'C') => str='abc', nb1=12.34, nb2=4.53e+15

    [var1, var2, var3... ] = strread(string, format {,n} {,'delimiter',delimiteur})
    (Analogue à textread présenté plus bas, mais agissant ici sur une string).
    Fonction de découpage de la chaîne string : chaque "spécification de conversion" définie dans le format est associée à une variable de sortie var-i qui sera du type correspondant ! Tant qu'il y a des données à lire, le format est réutilisé circulairement n fois, puis le découpage s'arrête. Si n n'est pas spécifié, le format est réutilisé autant de fois que nécessaire.
    Le découpage s'effectue là où il y a 1 ou plusieurs caractères espace. Mais on peut spécifier d'autres caractères de délimitation par la chaîne delimiteur. On utilisera la notation \... pour désigner certains caractères spéciaux, p.ex. \t pour le caractère tab.
    Les "spécification de conversion" suivantes peuvent être utilisées : %s pour identifier une chaîne ; %f ou %n pour un nombre double précision ; %d ou %u pour un entier 32bits ; %* pour sauter un mot ; string pour sauter la chaîne string.

    Ex:
    [str,nb1,nb2]=strread('abcde 1.23 4.56 fgh 7.98 10.1', '%s%f%f', 1) => str={'abcde'}, nb1=1.23, nb2=4.56 ; format utilisé 1x
    [str,nb1,nb2]=strread('abcde 1.23 4.56 fgh 7.98 10.1', '%s%f%f') => str={'abcde' ; 'fgh'}, nb1=[1.23 ; 7.98], nb2=[4.56 ; 10.1] ; format ici utilisé 2x
    [nb1,int2,nb3]=strread('1.23 ** 4.56 ## 7.89', '%f ** %u %* %f') => nb1=1.23, int2=5, nb3=7.89 ; '**' étant explicitement sauté, de même que '##' qui est matché par %*
    [nb1,nb2]=strread('1.2 # 3.4 * 5.6 # 7.8', '%f%f','delimiter','*#') => nb1=[1.2 ; 5.6], nb2=[3.4 ; 7.8] ; usage des délimiteurs espace, * et #



    Rappelons que si une chaîne ne contient que des nombres, on peut aussi aisément récupérer ceux-ci à l'aide de la fonction str2num présentée au chapitre sur les "Chaînes de caractères".
    Ex: str2num('12 34 ; 56 78') retourne la matrice [12 34 ; 56 78]

    Voir finalement aussi la fonction textscan qui est capable de lire des chaînes mais aussi des fichiers !

    Écriture formatée de variables sur une chaîne

    La fonction sprintf ("string print formated") lit les variables qu'on lui passe et les retourne, de façon formatée, sur une chaîne de caractère. S'il y a davantage d'éléments parmi les variables que de "spécifications de conversion" dans le format, le format sera "réutilisé" autant de fois que nécessaire.

    string = sprintf(format, variable(s)... )
    La variable string (de type chaîne) reçoit donc la(les) variable(s) formatée(s) à l'aide du format spécifié. Si, parmi les variables, il y a une ou plusieurs matrice(s), les éléments sont envoyés colonne après colonne.

    Ex:
    nb=4 ; prix=10 ; disp(sprintf('Nombre d''articles: %04u     Montant: %0.2f Frs', nb, nb*prix))
    ou, plus simplement: fprintf('Nombre d''articles: %04u     Montant: %0.2f Frs \n', nb, nb*prix)
        => affiche: Nombre d'articles: 0004     Montant: 40.00 Frs

    La fonction mat2str ("matrix to string") décrite ci-dessous (et voir chapitre "chaînes de caractères") est intéressante pour sauvegarder de façon compacte sur fichier des matrices sous forme texte (en combinaison avec fprintf) que l'on pourra relire sous MATLAB/Octave (lecture-fichier avec fscanf, puis affectation a une variable avec eval).

    string = mat2str(mat {,n})
    Convertit la matrice mat en une chaîne de caractère string incluant les crochets [ ] et qui serait dont "évaluable" avec la fonction eval. L'argument n permet de définir la précision (nombre de chiffres).

    Ex:
    str_mat = mat2str(eye(3,3)) produit la chaîne "[1 0 0;0 1 0;0 0 1]"
    • et pour affecter ensuite les valeurs d'une telle chaîne à une matrice x, on ferait eval(['x=' str_mat])

    Voir aussi les fonctions plus primitives int2str (conversion nombre entier->chaîne) et num2str (conversion nombre réel->chaîne).

    7.8.4 Lecture/écriture formatée de fichiers

    Lecture et écriture de fichiers


    14:56 min
    Quel que soit le langage utilisé (MATLAB/Octave, Python, C, Java...), la grande majorité des programmes que l'on développe sont appelés à manipuler des données qui doivent être persistantes, c'est-à-dire stockées sous forme de fichiers sur disque. Bien souvent aussi ces programmes ne travaillent pas seuls, c'est-à-dire qu'ils utilisent des données produites par d'autres logiciels, ou fournissent des données à d'autres programmes.
    Il existe fondamentalement 2 types de fichiers : les fichiers binaires et les fichiers texte :
    • les fichiers binaires ne sont pas lisibles par un oeil humain, et leur contenu n'est souvent pas documenté, donc seul le programme qui les a créé peut les utiliser (exemple: les fichiers au format natifs manipulés par un tableur tel que LibreOffice Calc ou Microsoft Excel...)
    • les fichiers-texte, quant à eux, sont directement lisibles par l'être humain et modifiables dans un éditeur ; et c'est ce type de fichier qui est le plus utilisé lorsqu'on échange des données entre différents programmes (exemple: le format CSV...)
    Nous présentons dans cette vidéo la lecture et l'écriture de fichiers texte sous MATLAB/Octave, et utilisons pour cela les "formats", concept présenté dans la vidéo précédente.

    Lecture de données numériques structurées

    S'il s'agit de lire/écrire des fichiers au format texte contenant des données purement numériques et avec le même nombre de données dans chaque ligne du fichier (i.e. des tableaux de nombres), la technique la plus efficace consiste à utiliser les fonctions suivantes décrites plus en détail au chapitre "Sauvegarde et chargement de données numériques via des fichiers-texte" :


    Lecture intégrale d'un fichier sur une chaîne

    string = fileread('file_name')
    Cette fonction lit d'une traite l'intégralité du fichier-texte nommé file_name et retourne son contenu sur un vecteur colonne string de type chaîne

    Ex: Dans l'exemple ci-dessous, la première instruction "avale" le fichier essai.txt sur le vecteur ligne de type chaîne fichier_entier (vecteur ligne, car on transpose le résultat de fileread). La seconde découpe ensuite cette chaîne selon les sauts de ligne (\n) de façon à charger le tableau cellulaire tabcel_lignes à raison d'une ligne du fichier par cellule.

    fichier_entier = fileread('essai.txt')' ;
    tabcel_lignes  = strread(fichier_entier, '%s', 'delimiter', '\n') ; 
    [status, string] = dos('type file_name')
    [status, string] = unix('cat file_name')
    Ces instructions lisent également l'intégralité du fichier-texte nommé file_name, mais le retournent sur un vecteur ligne string de type chaîne. On utilisera la première forme sous Windows, et la seconde sous Linux ou macOS.


    Fonction textread

    [vec1, vec2, vec3 ...] = textread(file_name, format {,n})
    (Analogue à strtread présenté plus haut, mais agissant dans ce cas sur un fichier entier). Fonction simple et efficace de lecture d'un fichier-texte nommé file_name dont l'ensemble des données répond à un format homogène. Les données peuvent être délimitées par un ou plusieurs espace, tab, voire même saut(s) de ligne newline. La lecture s'effectue ainsi en "format libre" (comme avec sscanf et fscanf).
    • Le vecteur-colonne vec1 recevra la 1ère "colonne" du fichier, le vecteur vec2 recevra la 2e colonne, vec3 la 3e, et ainsi de suite... La lecture s'effectue jusqu'à la fin du fichier, à moins que l'on spécifie le nombre n de fois que le format doit être réutilisé.
    • Le nombre de variables vec1 vec2 vec3... et leurs types respectifs découlent directement du format
    • Si vec-i réceptionne des chaînes de caractères, il sera de type "tableau cellulaire", en fait vecteur-colonne cellulaire

    Les "spécifications de conversion" de format %f, %s, %u et %d peuvent être utilisées avec textread sous MATLAB et Octave.
    Sous Octave seulement on peut en outre utiliser les spécifications %o et %x.
    Sous MATLAB, les spécifications %u et %d génèrent un vecteur réel double précision. Mais ATTENTION, sous Octave elles génèrent un vecteur entier 32 bits !
    Sous MATLAB seulement on peut encore utiliser :
          %[...] : lit la plus longue chaîne contenant les caractères énumérés entre [ ]
          %[^...] : lit la plus longue chaîne non vide contenant les caractèrens non énumérés entre [ ]

    Ex:
    Soit le fichier-texte de données ventes.txt suivant :

    10001     Dupond    Livres       12    23.50
    10002     Durand    Classeurs    15     3.95
    10003     Muller    DVDs          5    32.00
    10004     Smith     Stylos       65     2.55
    10005     Rochat    CDs          25    15.50
    10006     Leblanc   Crayons     100     0.60
    10007     Lenoir    Gommes       70     2.00
    et le script MATLAB/Octave suivant :
    [No_client, Nom, Article, Nb_articles, Prix_unit] = textread('ventes.txt', '%f %s %s %f %f') ;
    
    Montant = Nb_articles .* Prix_unit ;
    
    disp('     Client [No   ]      Nb  Articles     Prix unit.         Montant  ')
    disp('  --------- -------   -----  ---------   -----------      ------------')
    format = ' %10s [%d]   %5d  %-10s %8.2f Frs      %8.2f Frs\n' ;
    
    for no=1:1:length(No_client)
      fprintf(format, Nom{no}, No_client(no), Nb_articles(no), Article{no}, Prix_unit(no), Montant(no) ) ;
    end
    
    fprintf('\n\n                                               TOTAL      %8.2f Frs \n', sum(Montant) ) 
    Attention : bien noter, ci-dessus, les accolades pour désigner éléments de Nom{} et de Article{}. Ce sont des "tableaux cellulaires" dont on pourrait aussi récupérer les éléments, sous forme de chaîne, avec : char(Nom(no)), char(Article(no)).

    L'exécution de ce script: lit le fichier, calcule les montants, et affiche ce qui suit :

         Client [No   ]      Nb  Articles     Prix unit.         Montant  
      --------- -------   -----  ---------   -----------      ------------
         Dupond [10001]      12  Livres        23.50 Frs        282.00 Frs
         Durand [10002]      15  Classeurs      3.95 Frs         59.25 Frs
         Muller [10003]       5  DVDs          32.00 Frs        160.00 Frs
          Smith [10004]      65  Stylos         2.55 Frs        165.75 Frs
         Rochat [10005]      25  CDs           15.50 Frs        387.50 Frs
        Leblanc [10006]     100  Crayons        0.60 Frs         60.00 Frs
         Lenoir [10007]      70  Gommes         2.00 Frs        140.00 Frs
     
                                                   TOTAL       1254.50 Frs

    Fonctions classiques de manipulation de fichiers (de type ANSI C)

    file_id = fopen(file_name, mode)
    [file_id, message_err ] = fopen(file_name, mode)
    Ouvre le fichier de nom défini par la variable/chaîne file_name, et retourne l'identifiant file_id qui permettra de le manipuler par les fonctions décrites plus bas.

    Si file_name ne définit qu'un nom de fichier, celui-ci est recherché dans le répertoire courant. Si l'on veut ouvrir un fichier se trouvant dans un autre répertoire, il faut bien entendu faire précéder le nom du fichier de son chemin d'accès (path) relatif ou absolu. S'agissant du séparateur de répertoire, bien que celui-ci soit communément \ sous Windows, nous vous conseillons de toujours utiliser / (accepté par MATBAL/Octave sous Windows) pour que vos scripts/fonctions soient portables, c'est-à-dire utilisables dans les 3 mondes Windows, macOS et GNU/Linux.

    Le mode d'accès au fichier sera défini par l'une des chaînes suivantes :
    'rt' ou 'rb' ou 'r' : lecture seule (read)
    'wt' ou 'wb' ou 'w' : écriture (write), avec création du fichier si nécessaire, ou écrasement s'il existe
    'at' ou 'ab' ou 'a' : ajout à la fin du fichier (append), avec création du fichier si nécessaire
    'rt+' ou 'rb+' ou 'r+' : lecture et écriture, sans création
    'wt+' ou 'wb+' ou 'w+' : lecture et écriture avec écrasement du contenu
    'at+' ou 'ab+' ou 'a+' : lecture et ajout à la fin du fichier, avec création du fichier si nécessaire
    Le fait de spécifier t ou b ou aucun de ces deux caractères dans le mode a la signification suivante :
    t : ouverture en mode "texte"
    b ou rien : ouverture en mode "binaire" (mode par défaut)
    Sous Windows ou macOS, il est important d'utiliser le mode d'ouverture "texte" si l'on veut que les fins de ligne soient correctement interprétées !

    En cas d'échec (fichier inexistant en lecture, protégé en écriture, etc...), file_id reçoit la valeur "-1". On peut aussi récupérer un message d'erreur sous forme de texte explicite sur message_err

    Identifiants prédéfinis (toujours disponibles, correspondant à des canaux n'ayant pas besoin d'être "ouverts") :
    1 ou stdout : correspond à la sortie standard (standard output, c'est-à-dire fenêtre console MATLAB/Octave), pouvant donc être utilisé pour l'affichage à l'écran
    2 ou stderr : correspond au canal erreur standard (standard error, par défaut aussi la fenêtre console), pouvant aussi être utilisé par le programmeur pour l'affichage d'erreurs
    0 ou stdin : correspond à l'entrée standard (standard input, c'est-à-dire saisie au clavier depuis console MATLAB/Octave).

    Pour offrir à l'utilisateur la possibilité de désigner le nom et emplacement du fichier à ouvrir/créer à l'aide d'une fenêtre de dialogue classique (interface utilisateur graphique), on se référera aux fonctions uigetfile (lecture de fichier) et uiputfile (écriture de fichier) présentées au chapitre "Fenêtres de sélection de fichiers". Pour sélectionner un répertoire, on utilisera la fonction uigetdir.

    [file_name, mode] = fopen(file_id)
    Pour un fichier déjà ouvert avec l'identifiant file_id spécifié, retourne son nom file_name et le mode d'accès.
    freport()
    Affiche la liste de tous les fichiers ouverts, avec file_id, mode et file_name.
    On voit que les canaux stdin, stdout et stderr sont toujours pré-ouverts
    {status=} fclose(file_id)
    fclose('all')
    Referme le fichier ayant l'identifiant file_id (respectivement tous les fichiers ouverts). Le status retourné est "0" en cas de succès, et "-1" en cas d'échec.
    A la fin de l'exécution d'un script ayant ouvert des fichiers, tous ceux-ci sont automatiquement refermés, même en l'absence de fclose.
    variable = fscanf(file_id, format {,size})
    [variable, count] = fscanf(file_id, format {,size})
    Fonction de lecture formatée ("file scan formated") du fichier-texte spécifié par son identifiant file_id. Fonctionne de façon analogue à la fonction sscanf vue plus haut (à laquelle on renvoie le lecteur pour davantage de précision), sauf qu'on lit ici sur un fichier et non pas sur une chaîne de caractères.
    Remarque importante : en l'absence du paramètre size (décrit plus haut sous sscanf), fscanf tente de lire (avaler, "slurp") l'intégralité du fichier (et non pas seulement de la ligne courante comme fgetl ou fgets).

    Ex:
    Soit le fichier-texte suivant :

    10001     Dupond
      Livres      12    23.50
    10002     Durand
      Classeurs   15     3.95
    La lecture des données de ce fichier avec fscanf s'effectuerait de la façon suivante :
    file_id = fopen('fichier.txt', 'rt') ;
    no = 1 ;
    while ~ feof(file_id)
      No_client(no)   = fscanf(file_id,'%u',1) ;
      Nom{no,1}       = fscanf(file_id,'%s',1) ;
      Article{no,1}   = fscanf(file_id,'%s',1) ;
      Nb_articles(no) = fscanf(file_id,'%u',1) ;
      Prix_unit(no)   = fscanf(file_id,'%f',1) ;
      no = no + 1 ;
    end
    status = fclose(file_id) ;
    [variable, count] = scanf(format {,size})
    Fonction spécifiquement Octave de lecture formatée sur l'entrée standard (donc au clavier, identifiant 0). Pour le reste, cette fonction est identique à fscanf.
    line = fgetl(file_id)
    Lecture, ligne par ligne ("file get line"), du fichier-texte spécifié par l'identifiant file_id. A chaque appel de cette fonction on récupère, sur la variable line de type chaîne, la ligne suivante du fichier (sans le caractère de fin de ligne).
    string = fgets(file_id {,nb_car})
    Lecture, par groupe de nb_car ("file get string"), du fichier-texte spécifié par l'identifiant file_id. En l'absence du paramètre nb_car, on récupère, sur la variable string, la ligne courante inclu le(s) caractère(s) de fin de ligne (<cr> <lf> dans le cas de Windows).
    {count=} fskipl(file_id ,nb_lignes)
    Avance dans le fichier file_id en sautant nb_lignes ("file skip lines"). Retourne le nombre count de lignes sautées (qui peut être différent de nb_lignes si l'on était près de la fin du fichier).
    {status=} feof(file_id)
    Test si l'on a atteint la fin du fichier spécifié par l'identifiant file_id : retourne "1" si c'est le cas, "0" si non. Utile pour implémenter une boucle de lecture d'un fichier.

    Ex: voir l'usage de cette fonction dans l'exemple fscanf ci-dessus

    frewind(file_id)
    Se (re)positionne au début du fichier spécifié par l'identifiant file_id.
    Pour un positionnement précis à l'intérieur d'un fichier, voyez les fonctions :
    fseek(file_id, offset, origin) : positionnement offset octets après origin
    position = ftell(file_id) : retourne la position courante dans le fichier
    {count=} fprintf(file_id, format, variable(s)... )
    Fonction d'écriture formatée ("file print formated") sur un fichier-texte spécifié par l'identifiant file_id, et retourne le nombre count de caractères écrits. Fonctionne de façon analogue à la fonction sprintf vue plus haut (à laquelle on renvoie le lecteur pour davantage de précision), sauf qu'on écrit ici sur un fichier et non pas sur une chaîne de caractères.
    {count=} fprintf(format, variable(s)... )
    {count=} printf(format, variable(s)... )
    Utiliser fprintf en omettant le file_id (qui est est identique à utiliser le file_id "1" représentant la sortie standard) ou printf (spécifique à Octave), provoque une écriture/affichage à l'écran (i.e. dans la fenêtre de commande MATLAB/Octave).

    Ex: affichage de la fonction y=exp(x) sous forme de tableau avec :
    x=0:0.05:1 ; exponentiel=[x;exp(x)] ; fprintf('   %4.2f   %12.8f \n',exponentiel)

    {status=} fflush(file_id)
    Pour garantir de bonnes performances, Octave utilise un mécanisme de mémoire tampon (buffer) pour les opérations d'écriture. Les écritures ainsi en attente sont périodiquement déversées sur le canal de sortie, au plus tard lorsque l'on fait un fclose. Avec la fonction fflush, on force le vidage (flush) du buffer sur le canal de sortie.
    Dans des scripts interactifs ou affichant des résultats en temps réel dans la fenêtre console, il peut être utile dans certaines situations de flusher la sortie standard avec fflush(stdout). Voyez aussi la fonction page_output_immediately pour carrément désactiver le buffering.

    Fonction textscan

    Cette fonction est capable à la fois de lire un fichier ou de décoder une chaîne.
    vec_cel = textscan(file_id, format {,n})
    Lecture formatée d'un fichier-texte spécifié par l'identifiant file_id (voir ci-dessus) et dont l'ensemble des données répond à un format homogène.
    La lecture s'effectue jusqu'à la fin du fichier, à moins que l'on spécifie le nombre n de fois que le format doit être réutilisé.
    Dans le format, on peut notamment utiliser \r, \n ou \r\n pour matcher respectivement les caractères de fin de lignes "carriage-return" (macOS), "line-feed" (Unix/Linux) ou "carriage-return + line-feed (Windows)
    La fonction retourne un vecteur-ligne cellulaire vec_cel dont la longueur correspond au nombre de spécifications du format. Chaque cellule contient un vecteur-colonne de type correspondant à la spécification de format correspondante.

    Ex: on peut lire le fichier ventes.txt ci-dessus avec :

    file_id = fopen('ventes.txt', 'rt');
      vec_cel = textscan(file_id, '%u %s %s %u %f');
    fclose(file_id);
    et l'on récupère alors dans vec_cel{1} le vecteur de nombre des No, dans vec_cel{2} le vecteur cellulaire des Clients, dans vec_cel{3} le vecteur cellulaire des Articles, etc...
    vec_cel = textscan(string, format {,n})
    Opère ici sur la chaîne string

    7.8.5 Fonctions de lecture/écriture de fichiers spécifiques/binaires

    fread(...) et fwrite(...)
    Fonctions de lecture/écriture binaire (non formatée) de fichiers, pour un stockage plus compact qu'avec des fichiers en format texte. Voyez l'aide pour davantage d'information.
    xlsread(...) et xlswrite(...)
    Fonctions de lecture/écriture de classeurs Excel (binaire). Voyez l'aide pour davantage d'information.
    odsread(...) et odswrite(...)
    Fonctions de lecture/écriture de classeurs OpenOffice/LibreOffice (binaire). Voyez l'aide pour davantage d'information.

    7.9 "Publier" un code MATLAB/Octave

    7.9.1 La fonctionnalité "publish"

    Se rapprochant du concept de notebook, la fonction publish permet d'exécuter un script MATLAB/Octave en produisant un rapport comportant 3 parties :
    publish('script{.m}' {, 'format','fmt_rapport', 'imageFormat','fmt_images', 'showCode',true|false, 'evalCode',true|false } )
    Le rapport est créé dans le sous-répertoire nommé "html", mais on peut désigner un autre répertoire avec 'outputDir', 'path'

    Les paramètres optionnels sont :

    grabcode('URL')
    Récupère le code qui a été publié en HTML à l'URL spécifiée avec publish

    Ex: Soit le script ci-dessous nommé code.m :
    % Exécution/publish de ce code sous Octave
    
    % Données
        x= 4*rand(1,50) -2       % val. aléatoires entre -2 et +2
        y= 4*rand(1,50) -2       % val. aléatoires entre -2 et +2
        z= x.*exp(-x.^2 - y.^2)  % Z en ces points
    
    % Premier graphique (double, de type multiple plots)
        figure(1)
        subplot(1,2,1)
        plot(x,y,'o')
        title('semis de points aleatoire')
        grid('on')
        axis([-2 2 -2 2])
        axis('equal')
        subplot(1,2,2)
        tri_indices= delaunay(x, y);   % form. triangles => matr. indices
        trisurf(tri_indices, x, y, z)  % affichage triangles
        title('z = x * exp(-x^2 - y^2) % sur ces points')
        zlim([-0.5 0.5])
        set(gca,'ztick',-0.5:0.1:0.5)
        view(-30,10)
    
    % Second graphique (simple)
        figure(2)
        xi = -2:0.2:2 ;
        yi = xi';
        [XI,YI,ZI] = griddata(x,y,z,xi,yi,'nearest');  % interp. grille
        surfc(XI,YI,ZI)
        title('interpolation sur grille')
        

    Son exécution avec la commande :

      publish('code.m', 'format','html', 'imageFormat','png')

    produit le rapport accessible sous ce lien

    7.9.2 Les noteboks Jupyter

    Il s'agit d'une technique moderne et extrêmement élégante, que nous présentons dans un support de cours indépendant, permettant de mixer, dans un document vivant, du texte, du code et des graphiques.

    7.10 Debugging, profiling et optimisation de codes MATLAB/Octave

    7.10.1 Debugging

    Lorsqu'il s'agit de débuguer un script ou une fonction qui pose problème, la première idée qui vient à l'esprit est de parsemer le code d'instructions d'affichages intermédiaires. Plutôt que de faire des disp, on peut alors avantageusement utiliser la fonction warning présentée plus haut, celle-ci permettant en une seule instruction d'afficher du texte et des variables ainsi que de désactiver/réactiver aisément l'affichage de ces warnings. Mais il existe des fonctionnalités de debugging spécifiques présentées ci-après.

    Commandes de debugging simples

    echo   on | off
    echo   on all | off all
    • Active (on) ou désactive (off, c'est le cas par défaut) l'affichage/écho de toutes les commandes exécutées par les scripts
    • Active (on all) ou désactive (off all, c'est le cas par défaut) l'affichage/écho de toutes les commandes exécutées par les fonctions
    keyboard
    keyboard('prompt ')
    Placée à l'intérieur d'un M-file, cette commande invoque le mode de debugging "keyboard" de MATLAB/Octave : l'exécution du script est suspendue, et un prompt spécifique s'affiche (K>>, respectivement debug> ou le prompt spécifié). L'utilisateur peut alors travailler normalement en mode interactif dans MATLAB/Octave (visualiser ou changer des variables, passer des commandes...). Puis il a le choix de :
    • continuer l'exécution du script en frappant en toutes lettres la commande return
    • ou avorter la suite du script en frappant la commande dbquit sous MATLAB ou Octave
    Ce mode "keyboard" permet ainsi d'analyser manuellement certaines variables en cours de déroulement d'un script.

    Debugging en mode graphique

    Les éditeurs/débuggers intégrés de MATLAB et de Octave GUI (depuis Octave 3.8) permettent de placer visuellement des breakpoints (points d'arrêt) dans vos scripts/fonctions, puis d'exécuter ceux-ci en mode step-by-step (instructions pas à pas). L'intérêt réside dans le fait que lorsque l'exécution est suspendue sur un breakpoint ou après un step, vous pouvez, depuis la fenêtre de console à la suite du prompt K>> ou debug>, passer interactivement toute instruction MATLAB/Octave (p.ex. vérifier la valeur d'une variable, la modifier...), puis poursuivre l'exécution. Par rapport à la méthode keyboard ci-dessus, l'avantage ici est qu'on ne "pollue" pas notre code d'instructions provisoires.

    Les boutons décrits ci-dessous se cachent dans l'onglet EDITOR du bandeau MATLAB, et dans palette d'outils de l'éditeur intégré de Octave GUI.

    A) Mise en place de breakpoints :

    B) Puis exécution step-by-step :

    Fonctions de debugging

    Les fonctions ci-dessous sont implicitement appelées lorsque l'on fait du debugging en mode graphique (chapitre précédent), mais vous pouvez aussi les utiliser depuis la console MATLAB/Octave.

    A) Mise en place de breakpoints :

    dbstop in script|fonction at no
    dbstop('script|fonction', no {, no, no...} )   ou dbstop('script|fonction', vecteur_de_nos)
    Défini (ajoute), lors de l'exécution ultérieure du script ou de la fonction indiquée, des breakpoints au début des lignes de no spécifié
    L'avantage, par rapport à l'instruction keyboard décrite précédemment, est qu'ici on ne "pollue" pas notre code, les breakpoints étant définis interactivement avant l'exécution
    dbclear in script|fonction at no   respectivement dbclear in script|fonction
    dbclear('script|fonction', no {, no, no...} )   respectivement dbclear('script|fonction')
    Supprime, dans le script ou la fonction indiquée, les breakpoints précédemment définis aux lignes de no spécifié.
    Dans sa seconde forme, cette instruction supprime tous les breakpoints relatif au script/fonction spécifié.
    struct = dbstatus {script|fonction}
    struct = dbstatus {('script|fonction')}
    Affiche (ou retourne sur une structure) les vecteurs contenant les nos de ligne sur lesquels sont couramment définis des breakpoints. Si on ne précise pas de script/fonction, retourne les breakpoints de tous les scripts/fonctions
    B) Puis exécution en mode step-by-step à l'aide des fonctions MATLAB/Octave suivantes : S'agissant d'exécution de scripts/fonctions imbriqués, on peut encore utiliser les commandes de debugging dbup, dbdown, dbstack...

    7.10.2 Profiling

    Sous le terme de "profiling" on entend l'analyse des performances d'un programme (script, fonctions) afin d'identifier les parties de code qui pourraient être optimisées dans le but d'améliorer les performances globales du programme. Les outils de profiling permettent ainsi de comptabiliser de façon fine (au niveau script, fonctions et même instructions) le temps consommé lors de l'exécution du programme, puis de présenter les résultats de cette analyse sous forme de tableaux et d'explorer ces données.

    Pour déterminer le temps CPU utilisé dans certaines parties de vos scripts ou fonctions, une alternative aux outils de profiling ci-dessous serait d'ajouter manuellement dans votre code des fonctions de "timing" (chronométrage du temps consommé) décrites au chapitre "Dates et temps", sous-chapitre "Fonctions de timing et de pause".

    Commandes liées au profiling

    Enclencher/déclencher le processus de profiling :

    profile on   ou   profile('on')
    profile resume   ou   profile('resume')
    Démarre le profiling, c'est-à-dire la comptabilisation du temps consommé :
    • avec on : efface les données de profiling précédemment collectées
    • avec resume : reprend le profiling qui a été précédemment suspendu avec profile off, sans effacer données collectées
    profile off   ou   profile('off')
    Interrompt ou suspend la collecte de données de profiling, afin d'en exploiter les données
    stats_struct = profile('status')
    Retourne la structure stats_struct dans laquelle le champ ProfilerStatus indique si le profiling est couramment activé (on) ou stoppé (off)
    prof_struct = profile('info')
    Récupère sur la structure prof_struct les données de profiling collectées
    profile clear
    Efface toutes les données de profiling collectées
    Explorer sous MATLAB les données de profiling :
    profile viewer   ou   profile('viewer')   ou   profview
    Interrompt le profiling (effectue implicitement un profile off) et ouvre l'explorateur de profiling (le "Profiler"). Il s'agit d'une fenêtre MATLAB spécifique dans laquelle les données de profiling sont hiérarchiquement présentées sous forme HTML à l'aide de liens hyper-textes.
    Explorer sous Octave les données de profiling :
    profexport(path, {nom,} prof_struct) (depuis Octave 4.2)
    Exporte les données de profiling sous forme d'un ensemble de fichiers HTML dans le répertoire path. On peut alors explorer ces données confortablement en chargeant le fichier path/index.html dans un navigateur web et en suivant les liens hyper-textes.
    profshow(prof_struct {, N })
    Affiche sous forme de tableau, dans l'ordre descendant du temps consommé, les données de profiling prof_struct. On peut spécifier le nombre N de fonctions/instructions affichées. Si N n'est pas spécifié, ce sera par défaut 20.
    profexplore(prof_struct)
    Explore interactivement, dans la fenêtre de commande Octave, les données hiérarchiques de profiling prof_struct
    help : aide en-ligne sur les commandes disponibles
    opt : descend d'un niveau dans l'option opt spécifiée
    up {nb_niv} : remonte d'un niveau, ou de nb_niv niveaux ; si l'on est au premier niveau, retourne au prompt Octave
    exit : interrompt cette exploration interactive

    Illustration par un exemple

    Soit le script et les 2 fonctions suivants :

    Script demo_profiling.m :
    %DEMO_PROFILING   Script illustrant, par
    %  profiling, l'utilité de vectoriser son code !
    
    t0=cputime;      % compteur temps CPU consommé
    
    % Génération matrice aléatoire
    nb_l = 1000;
    nb_c =  500;
    v_min = -10 ;
    v_max =  30 ;
    mat = matrice_alea(nb_l, nb_c, v_min, v_max) ;
    
    % Extraction de certains éléments de mat
    vmin =  0 ;
    vmax = 20 ;
    vec = extrait_matrice(mat, vmin, vmax) ;
    
    % Calcul de la moyenne des éléments extraits
    if CODE_VECTORISE  % Code vectorisé  :-)
      moyenne_vect = mean(vec) ;
    else               % Code non vectorisé  :-(
      somme_elem = 0 ;
      for indice=1:length(vec)
        somme_elem = somme_elem + vec(indice) ;
      end
      moyenne_vect = somme_elem / length(vec) ;
    end
    
    % Affichages...
    moyenne_vect
    duree_calcul=cputime-t0
    
    Fonction matrice_alea.m :
    function [matrice]=matrice_alea(nb_l,nb_c,v_min,v_max)
    % MATRICE_ALEA(NB_L, NB_C, V_MIN, V_MAX)
    %   Generation matrice de dimension (NB_L, NB_C)
    %   de nb aleatoires compris entre V_MIN et V_MAX
    
      global CODE_VECTORISE
      v_range = v_max - v_min ;
      
      if CODE_VECTORISE  % Code vectorisé  :-)
        matrice = (rand(nb_l, nb_c) * v_range) + v_min ;
      
      else               % Code non vectorisé  :-(
        for lig=1:nb_l
          for col=1:nb_c
            matrice(lig,col) = (rand()*v_range) + v_min ;
          end
        end
      end
    return
    
    Fonction extrait_matrice.m :
    function [vecteur] = extrait_matrice(mat, vmin, vmax)
    % EXTRAIT_MATRICE(MAT, VMIN, VMAX)
    %   Extrait de la matrice MAT, parcourue col. apres
    %   colonne, tous les elements dont la val. est
    %   comprise entre VMIN et VMAX, et les retourne
    %   sur un vecteur colonne
    
      global CODE_VECTORISE
    
      if CODE_VECTORISE  % Code vectorisé (index. logique) :-)
        vecteur=mat(mat>=vmin & mat<=vmax);
      
      else               % Code non vectorisé  :-(
        indice = 1;
        for col=1:size(mat,2)
          for lig=1:size(mat,1)
            if (mat(lig,col) >= vmin) && (mat(lig,col) <= vmax)
              vecteur(indice) = mat(lig,col) ;
              indice = indice + 1 ;
            end
          end
        end
        vecteur = vecteur' ;
      end
    return
    

    Vous constatez que, pour les besoins de l'exemple, ces codes comportent du code vectorisé (usage de fonctions vectorisées, indexation logique...) et du code non vectorisé (usage de boucles for/end). Nous avons délimité ces deux catégories de codes par des structures if/else/end s'appuyant sur une variable globale nommée CODE_VECTORISE.

    Réalisons maintenant le profiling de ce code. En premier lieu, il faut rendre globale au niveau workspace et du script la variable CODE_VECTORISE (ceci est déjà fait dans les fonctions), avec l'instruction :

    Les durées d'exécution indiquées ci-dessous se rapportent à une machine Intel Pentium Core 2 Duo E6850 @ 3 GHz avec MATLAB R2012 et GNU Octave 3.6.2 MinGW.

    Commençons par l'examen du code non vectorisé :

    1. CODE_VECTORISE=false; % on choisit donc d'utiliser ici le code non vectorisé
    2. profile on % démarrage du profiling
    3.     profile status % contrôle du statut du profiling (il est bien à on)
    4. demo_profiling % exécution de notre script
    5. profile off % interruption de la collecte de données de profiling
    6.     profile status % contrôle du statut du profiling (il est bien à off)
    1. Puis sous MATLAB :
        profile viewer % ouverture de l'explorateur de profiling
      Constatations :
        - le script s'est exécuté en 9 sec sous Linux/Ubuntu, et étonnamment en 1 sec sous Windows !

    2. Ou sous Octave :
        prof_struct=profile('info'); % récupération des données de profiling
        profexport('profiling', prof_struct) % génération, dans le sous-répertoire profiling, d'un rapport que l'on peut explorer interactivement
          depuis un navigateur web en chargeant le fichier index.html
        ou profshow(prof_struct) % affichage tabulaire des 20 plus importants données (en temps) de profiling
          puis profexplore(prof_struct) % exploration interactive des données de profiling ; "descendre" ensuite avec 1 dans "demo_profiling", puis dans
          les fonctions "matrice_alea" et "extrait_matrice" ...
      Constatations :
        - le script s'est exécuté en 14 sec sous Linux, et en 23 sec sous Windows
        - sous Windows p.ex. 12 sec sont consommées par la fonction "matrice_alea" (dont 4.5 sec par la fonction "rand"), 9 sec par la fonction "extrait_matrice"
        - on constate notamment que la fonction "rand" est appelée 500'000x
        - on voit donc ce qu'il y a lieu d'optimiser...
    Essayez de relancer ce script sans profiling. Vous constaterez qu'il s'exécute un peu plus rapidement, ce qui montre que le profiling lui-même consomme du temps CPU, et donc qu'il ne faut l'activer que si l'objectif est bien de collecter des données de performance !

    Poursuivons maintenant avec l'examen du code vectorisé (remplacement des boucles par des fonctions vectorisées et l'indexation logique) :

    1. CODE_VECTORISE=true; % on choisit donc d'utiliser ici le code vectorisé
    2. profile on % démarrage du profiling
    3. demo_profiling % exécution de notre script
    4. profile off % interruption de la collecte de données de profiling
    1. Puis sous MATLAB :
        profile viewer % ouverture de l'explorateur de profiling
      Constatations :
        - le script s'est exécuté en 0.1 sec sous Linux/Ubuntu et sous Windows !
        - on a donc gagné d'un facteur de plus de 100x par rapport à la version non vectorisée !

    2. Ou sous Octave :
        prof_struct=profile('info'); % récupération des données de profiling
        profexport('profiling', prof_struct) % génération, dans le sous-répertoire profiling, d'un rapport que l'on peut explorer interactivement
          depuis un navigateur web en chargeant le fichier index.html
        ou profshow(prof_struct) % affichage tabulaire des 20 plus importants données (en temps) de profiling
          puis profexplore(prof_struct) % exploration interactive des données de profiling (comme plus haut...)
      Constatations :
        - le script s'est exécuté en 0.1 sec sous Windows, et 0.05 sec sous Linux/Ubuntu !
        - on a donc gagné d'un facteur de plus de 200x par rapport à la version non vectorisée !

    7.10.3 Optimisation

    Il existe de nombreuses techniques pour optimiser un code MATLAB/Octave en terme d'utilisation des ressources processeur et mémoire. Nous donnons ci-après quelques conseils de base. Notez cependant qu'il faut parfois faire la balance entre en optimisation et lisibilité du code.

    Optimisation du temps de calcul (CPU)

    Optimisation de l'utilisation mémoire (RAM)




    Documentation © CC BY-SA 4.0 / / EPFL / Rév. 16-09-2021       ↵ Table des matières