%%  Format et mise en page 

\def\presentation{
\voffset -.50in  \hoffset -.19in
\oddsidemargin 0in \evensidemargin 0in
\marginparwidth .75in \marginparsep 7pt \topmargin 0in
\headheight 12pt \headsep .25in
\footheight 18pt \footskip .35in
\textheight 9.5in \textwidth 6.5in
\columnsep 10pt \columnseprule 0pt }

\def\program#1{{\it{#1}}}
\def\programwithlink#1#2{{\it{{#1}/{#2}}}}
\def\href#1#2{#1\footnote{#2}}
\def\option#1{-#1}
\def\longoption#1{- -#1}
\def\filename#1{{\bf{#1}}}
\def\filepath#1{{\bf{#1}}}

\def\bookref#1{{\it{#1}}}
\def\errormessage#1{{\it{#1}}}
\def\directoryname#1{{\it{#1}}}
\def\command#1{{\it{#1}}}
\def\codeline#1{{\it{#1}}}
\def\function#1{{\it{#1}}}
\def\alert#1{{\bf{#1}}}
\def\commandarray#1{\begin{center}{\it{#1}}\end{center}}
\def\paperwithlink#1#2{{\it{#1}}}
\def\equline#1{\begin{math}#1\end{math}}

\documentclass[a4paper,11pt,twoside,openright]{book}
\voffset -.50in  \hoffset -.19in
\oddsidemargin 0in \evensidemargin 0in
\marginparwidth .75in \marginparsep 7pt \topmargin 0in
\headheight 12pt \headsep .25in
\textheight 9.5in \textwidth 6.5in
\columnsep 10pt \columnseprule 0pt 

\usepackage[francais]{babel}
\usepackage[french]{minitoc}
\usepackage{longtable}

\begin{document}


\setcounter{chapter}{5}

\chapter{Les fonctions}

\section{G\'en\'eralit\'es}


D\`es que votre programme d\'epasse la cinquantaine de lignes, il y a de fortes chances que vous puissiez mettre certaines partie du code dans des fonctions. Les fonctions sont imp\'eratives lorsque vous voulez faire des appels r\'ecursifs par exemple. Mais elles \'evitent surtout d'avoir du code redondant. N'oubliez jamais que si vous r\'ep\'etez plusieurs fois un ensemble d'instructions et qu'une erreur s'y est gliss\'ee, ce sera autant de corrections \`a faire (rappelez vous, jeunes apprentis, des trois qualit\'es de l'informaticien). 

Comment \'ecrire une fonction en PERL? A la fin de votre programme principal, il vous suffit de d\'eclarer vos fonctions de la sorte:\\

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&sub mafonction\\
2.&\hspace{0.25cm}\{\\
3.&\hspace{0.5cm}instrutions;\\
4.&\hspace{0.5cm}...\\
5.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


Vous pouvez mettre \`a la suite autant de fonctions que vous le souhaitez. Pour utiliser cette fonction, il vous suffit de l'appeler par son nom pr\'ec\'ed\'e du signe \& (non obligatoire mais recommand\'e). Voyons ceci sur un petit exemple:

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&print ("debut de la recherche...{\textbackslash}n");\\
4.&\&cherchefichier;\\
5.&print ("fin de la recherche.{\textbackslash}n");\\
6.&\\
7.&sub cherchefichier\\
8.&\hspace{0.25cm}\{\\
9.&\hspace{0.5cm}foreach {\textdollar}fichier({\textless}*.tex{\textgreater})\\
10.&\hspace{0.75cm}\{\\
11.&\hspace{1cm}print ({\textdollar}fichier."{\textbackslash}n");\\
12.&\hspace{0.75cm}\}\\
13.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


Ce petit programme affiche tous les fichiers ayant l'extension \filename{.tex} contenus dans le r\'epertoire courant. La fonction \codeline{cherchefichier} se charge d'imprimer tous les fichiers ayant la bonne extension. Si vous souhaitez chercher de mani\`ere r\'ecursive dans tous les sous-r\'epertoires \`a partir du r\'epertoire courant, voici comment modifier la fonction \codeline{cherchefichier}:\\

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&print ("debut de la recherche...{\textbackslash}n");\\
4.&\&cherchefichier;\\
5.&print ("fin de la recherche.{\textbackslash}n");\\
6.&\\
7.&sub cherchefichier\\
8.&\hspace{0.25cm}\{\\
9.&\hspace{0.5cm}foreach {\textdollar}fichier({\textless}*{\textgreater})\\
10.&\hspace{0.75cm}\{\\
11.&\hspace{1cm}if (-d {\textdollar}fichier)\\
12.&\hspace{1.25cm}\{\\
13.&\hspace{1.5cm}chdir ({\textdollar}fichier);\\
14.&\hspace{1.5cm}\&cherchefichier;\\
15.&\hspace{1.5cm}chdir ("..");\\
16.&\hspace{1.25cm}\}\\
17.&\hspace{1cm}else\\
18.&\hspace{1.25cm}\{\\
19.&\hspace{1.5cm}if ({\textdollar}fichier ={\textasciitilde} /{\textbackslash}.tex{\textdollar}/)\\
20.&\hspace{1.75cm}\{\\
21.&\hspace{2cm}print ({\textdollar}fichier."{\textbackslash}n");\\		
22.&\hspace{1.75cm}\}\\
23.&\hspace{1.25cm}\}\\
24.&\hspace{0.75cm}\}\\
25.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


A la ligne 11, dans la boucle qui regarde le contenu du r\'epertoire courant, la fonction teste si le fichier en cours d'analyse est un r\'epertoire. Si c'est le cas, la fonction se place dans ce r\'epertoire (ligne 12) et s'appelle elle-m\^eme pour rechercher dans ce qui est maintenant le nouveau r\'epertoire courant. Au retour, la fonction se replace dans le r\'epertoire et l'exploration continue. Si le fichier analys\'e n'est pas un r\'epertoire, la fonction regarde si l'extension est celle recherch\'ee (ligne 18) et affiche le nom du fichier dans l'affirmative.

Comme dans la plupart des langages, il est possible de passer des param\`etres \`a la fonction. Au moment de l'appel, il suffit d'indiquer ces param\`etres entre parenth\`eses apr\`es le nom de la fonction, s\'epar\'es par des virgules. Ces param\`etres sont ensuite r\'ecup\'er\'es dans la fonction par l'interm\'ediaire du tableau par d\'efaut, dans l'ordre dans lequel ils ont \'et\'e pass\'es. De m\^eme, il est possible de retourner une valeur \`a la fin de l'ex\'ecution de la fonction. Le programme suivant va servir \`a illustrer notre propos:\\

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&{\textdollar}ligne = "3.14 133 1.6";\\
4.&{\textdollar}ligne .= " ".\&divise({\textdollar}ligne, 0, 2);\\
5.&print ({\textdollar}ligne."{\textbackslash}n");\\
6.&\\
7.&sub divise\\
8.&\hspace{0.25cm}\{\\
9.&\hspace{0.5cm}@tableau = split (/ /, {\textdollar}\_[0]);\\
10.&\hspace{0.5cm}return ({\textdollar}tableau[{\textdollar}\_[1]] / {\textdollar}tableau[{\textdollar}\_[2]]);\\
11.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


Ce petit script nous permet, \`a partir d'une ligne de texte contenant des nombres s\'epar\'es par un espace, d'effectuer l'op\'eration de division de l'un de ces nombres par un autre. A la ligne 3, nous d\'efinissons la ligne \`a traiter avec juste trois nombres (mais nous aurions pu en avoir bien plus). La ligne 4 fait appel \`a la fonction \codeline{divise} et concat\`ene le r\'esultat retourn\'e \`a un espace puis \`a la variable \codeline{{\textdollar}ligne} rajoutant le r\'esultat \`a la liste des nombres d\'ecrits par cette variable. Regardons de plus pr\`es la fonction divise. Le premier argument est la cha\^{\i}ne de caract\`eres que nous souhaitons traiter. Elle est contenue dans la variable \codeline{{\textdollar}\_[0]}. Gr\^ace \`a la fonction  \function{split}, nous r\'ecup\'erons chacun des nombres dans le tableau \codeline{@tableau}. La commande \function{return} retourne la valeur, variable ou tableau, qui la suit.Vous ne pouvez retourner qu'une seule valeur. Si vous voulez retourner plusieurs variables, il est n\'ecessaire de les stocker au pr\'ealable dans un tableau puis de retourner ce dernier. Dans notre exemple, nous retournons le r\'esultat de l'op\'eration de la division du nombre dont la position est rep\'er\'ee par le contenu de la variable pass\'ee en second argument et qui est donc stock\'ee dans \codeline{{\textdollar}\_[1]}, par le nombre dont la position est rep\'er\'ee par le contenu de la variable pass\'ee en troisi\`eme argument (stock\'e dans \codeline{{\textdollar}\_[2]}).

Pour ceux qui se risqueraient \`a passer un tableau, ils auront la d\'esagr\'eable surprise de voir les \'el\'ements de leur tableau inclus un par un dans le tableau par d\'efaut: PERL ins\`ere le tableau lors du passage \`a une fonction. Pour passer un tableau en tant qu'entit\'e, il est n\'ecessaire de passer par les r\'ef\'erences que nous d\'ecrirons \`a la fin de ce chapitre.



\section{Port\'ee des variables}




Pour ceux qui connaissent d\'ej\`a d'autres langages, la question qui vient naturellement \`a l'esprit lorsque nous parlons des fonctions est celle concernant la port\'ee des variables (dans cette section le terme variable couvrira aussi les tableaux, les tables de hash,...). Regardons le programme qui suit:\\

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&{\textdollar}variable = 1.0;\\
4.&print ({\textdollar}variable."{\textbackslash}n");\\
5.&\&incremente();\\
6.&print ({\textdollar}variable."{\textbackslash}n");\\
7.&\\
8.&sub incremente\\
9.&\hspace{0.25cm}\{\\
10.&\hspace{0.5cm}{\textdollar}variable++;\\
11.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


Qu'affiche le programme?
\begin{center}\begin{longtable}{|rl|}
\hline
&{\it 1}\\
&{\it 2}\\
\hline
\end{longtable}
\end{center}


Nous observons que la variable \codeline{{\textdollar}variable} a \'et\'e modifi\'ee par la fonction. Par d\'efaut, la port\'ee des variables en PERL est globale: une variable d\'efinie \`a un endroit du programme peut \^etre utilis\'ee et modifi\'ee \`a n'importe quel autre endroit, y compris dans une fonction. A l'oppos\'ee des variables globales, nous trouvons les variables locales dont l'utilisation ou la modification n'est possible que dans le bloc d'instruction o\`u elle est d\'efinie et les sous-blocs de ce bloc. Par exemple, une variable locale d\'efinie dans la partie principale du programme n'est pas accessible depuis une fonction, la r\'eciproque \'etant aussi valable. Cela permet d'utiliser \`a deux endroits diff\'erents le m\^eme nom de variable sans pour autant se rapporter au m\^eme contenu. Si cela ne vous parait pas limpide, regardez la version modifi\'ee du programme pr\'ec\'edent:\\

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&my {\textdollar}variable = 1.0;\\
4.&print ({\textdollar}variable."{\textbackslash}n");\\
5.&\&incremente();\\
6.&print ({\textdollar}variable."{\textbackslash}n");\\
7.&\\
8.&sub incremente\\
9.&\hspace{0.25cm}\{\\
10.&\hspace{0.5cm}my {\textdollar}variable = 1.0;\\
11.&\hspace{0.5cm}{\textdollar}variable++;\\
12.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


Le programme affiche maintenant
\begin{center}\begin{longtable}{|rl|}
\hline
&{\it 1}\\
&{\it 1}\\
\hline
\end{longtable}
\end{center}


Vous pouvez constater que m\^eme si nous avons d\'efini deux variables avec des noms identiques ligne 3 (dans la partie principale du programme) et 10 (dans la fonction \codeline{incremente}), il s'agit bien de variables distinctes. Pour d\'efinir une variable locale, il suffit, avant de l'utiliser, de la d\'eclarer (comprendre indiquer son nom) avec l'instruction \function{my}. Dans la d\'eclaration, vous pouvez aussi effectuer une affectation comme nous l'avons fait dans notre exemple mais cela n'a pas de caract\`ere obligatoire.

Pourquoi ne pas travailler qu'avec des variables globales? La r\'eponse sera d\'etaill\'ee au prochain chapitre et concerne la difficult\'e de g\'erer des variables globales d\`es que le programme atteint une certaine taille. 



\section{Passage par r\'ef\'erence}


(N.B.: cette section peut \^etre r\'eserv\'ee \`a une seconde lecture)

Si vous travaillez uniquement avec des variables locales, vous serez vite confront\'e au probl\`eme suivant: demander \`a une fonction de modifier directement le contenu d'une variable. La solution fait appel au passage par r\'ef\'erence. Cette technique n'est pas \'evidente et rappellera pour certains le cauchemar des pointeurs en C. L'id\'ee est de passer non plus la valeur, mais l'endroit o\`u elle se situe. Regardons sur l'exemple suivant comment cela fonctionne:\\

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&my {\textdollar}variable = 1.0;\\
4.&print ({\textdollar}variable."{\textbackslash}n");\\
5.&\&incremente({\textbackslash}{\textdollar}variable);\\
6.&print ({\textdollar}variable."{\textbackslash}n");\\
7.&\\
8.&sub incremente\\
9.&\hspace{0.25cm}\{\\
10.&\hspace{0.5cm}my {\textdollar}parametre = {\textdollar}\_[0];\\
11.&\hspace{0.5cm}{\textdollar}{\textdollar}parametre++;\\
12.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


Si vous ex\'ecutez ce code, il affichera 1 puis 2. A la ligne 5, lorsque nous appelons la fonction \codeline{incremente}, nous avons mis un backslash devant le nom de la variable que nous voulons passer. Par cette syntaxe, nous passons une r\'ef\'erence sur la variable et non la variable elle-m\^eme. Dans la fonction, nous commen\c{c}ons par r\'ecup\'erer cette r\'ef\'erence dans la variable \codeline{{\textdollar}parametre}. Apr\`es cette affectation, la variable contient une r\'ef\'erence et non le contenu de la variable pass\'ee par r\'ef\'erence. Ceci signifie que vous ne pouvez utiliser directement \codeline{{\textdollar}parametre}. De fa\c{c}on imag\'ee, vous pouvez supposer que \codeline{{\textdollar}parametre} contient l'adresse ou le num\'ero de casier o\`u se situe la variable \codeline{{\textdollar}variable}.

A la ligne 11, nous souhaitons incr\'ementer la variable r\'ef\'erenc\'ee par \codeline{{\textdollar}parametre}. Pour pouvoir acc\'eder \`a la variable elle-m\^eme, c'est \`a dire pour pouvoir agir sur le contenu du casier, il faut indiquer \`a PERL de quel type de variable, il s'agit: variable simple, tableau, table de hash,... Pour cela, il suffit de rajouter le symbole correspondant devant la r\'ef\'erence, ce qui explique le double signe {\textdollar} devant le nom de la r\'ef\'erence (un pour la r\'ef\'erence et un pour indiquer le type). Ainsi \codeline{{\textdollar}{\textdollar}parametre} devient strictement \'equivalent \`a \codeline{{\textdollar}variable}.

Dans le cas d'un tableau, voici comment se passe le passage par r\'ef\'erence:\\

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&my @tableau = (1, 4, 9, 16);\\
4.&print (join(" ", @tableau)."{\textbackslash}n");\\
5.&\&ajoute({\textbackslash}@tableau);\\
6.&print (join(" ", @tableau)."{\textbackslash}n");\\
7.&\\
8.&sub ajoute\\
9.&\hspace{0.25cm}\{\\
10.&\hspace{0.5cm}my {\textdollar}parametre = {\textdollar}\_[0];\\
11.&\hspace{0.5cm}push (@{\textdollar}parametre, 25);\\
12.&\hspace{0.5cm}{\textdollar}{\textdollar}parametre[5] = 36;\\
13.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}




Les r\'ef\'erences ont d'autres utilit\'es que celle de permettre de donner acc\`es \`a des variables locales externes \`a des fonctions. Vous pouvez tout \`a fait stocker des r\'ef\'erences dans un tableau (ou une table de hash). En particulier, vous pouvez faire en sorte que chaque \'el\'ement d'un tableau soit une r\'ef\'erence vers un autre tableau. Si vous avez compris les r\'ef\'erences, cette derni\`ere phrase doit sonner dans votre t\^ete comme une r\'ev\'elation: vous venez de cr\'eer un tableau multidimensionnel!

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&my @matrice ;\\
4.&for ({\textdollar}indexi = 0; {\textdollar}indexi {\textless} 10; {\textdollar}indexi++)\\
5.&\hspace{0.25cm}\{\\
6.&\hspace{0.5cm}my @colonne;\\
7.&\hspace{0.5cm}for ({\textdollar}indexj = 0; {\textdollar}indexj {\textless} 10; {\textdollar}indexj++)\\
8.&\hspace{0.75cm}\{\\
9.&\hspace{1cm}push (@colonne, {\textdollar}indexi + {\textdollar}indexj);\\
10.&\hspace{0.75cm}\}\\
11.&\hspace{0.5cm}push (@matrice, \@colonne);\\
12.&\hspace{0.25cm}\}\\
13.&\\
14.&for ({\textdollar}indexi = 0; {\textdollar}indexi {\textless} 10; {\textdollar}indexi++)\\
15.&\hspace{0.25cm}\{\\
16.&\hspace{0.5cm}my {\textdollar}reference = {\textdollar}matrice[{\textdollar}indexi];\\
17.&\hspace{0.5cm}for ({\textdollar}indexj = 0; {\textdollar}indexj {\textless} 10; {\textdollar}indexj++)\\
18.&\hspace{0.75cm}\{\\
19.&\hspace{1cm}print ({\textdollar}{\textdollar}reference[{\textdollar}indexj];\\
20.&\hspace{0.75cm}\}\\
21.&\hspace{0.5cm}print ("{\textbackslash}n");\\
22.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


Ce programme est compos\'e de deux s\'equences. Des lignes 3 \`a 12, nous cr\'eons le tableau \`a deux dimensions (i.e. une matrice). \codeline{@matrice} va contenir les r\'eferences des diff\'erentes colonnes de notre matrice. A chaque it\'eration sur les colonnes (\codeline{{\textdollar}indexi}), nous commen\c{c}ons par cr\'eer un tableau \codeline{@colonne} que nous remplissons. Nous ajoutons ensuite la r\'ef\'erence sur ce tableau \`a \codeline{@matrice}. Pour parcourir la matrice (lignes 14 \`a 22), nous commen\c{c}ons par r\'ecup\'erer la r\'ef\'erence (ligne 16) puis nous pratiquons de la m\^eme mani\`ere que celle indiqu\'ee dans le programme pr\'ec\'edent pour r\'ecup\'erer les \'el\'ements du tableau.

Bien que l'envie soit grande d'user et d'abuser des tableaux multidimensionnels, nous indiquons au lecteur que nous ne pourrions \^etre tenus responsables des migraines provoqu\'ees par l'utilisation excessive de ces derniers. Bien que puissante la notion de r\'ef\'erence n'est pas la plus ais\'ee \`a comprendre et \`a manipuler.



\section{Exercices}



\begin{itemize}
\item[1] Reprendre l'exercice 2 du chapitre 5 qui consistait \`a renommer les fichiers en effectuant aussi l'op\'eration dans tous les sous-r\'epertoires.
\item[2] Faire une fonction de recherche de minimum et maximum dans un tableau. Les valeurs seront retourn\'ees en utilisant soit les r\'ef\'erence soit un tableau. Vous pourrez utiliser tester cette fonction en l'ins\'erant dans le programme de l'exercice 3 du chapitre5.
\end{itemize}





\end{document}


