533 lines
12 KiB
Plaintext
533 lines
12 KiB
Plaintext
Les fonctions et les procédures
|
||
================================
|
||
|
||
Préliminaire : rappel de théorie de l'information
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Théorie de l'information (Claude Shannon, 1949), (ou théorie de la communication)
|
||
|
||
Canal de transmission::
|
||
|
||
entrée -> récepteur -> émetteur -> sortie
|
||
|
||
Description d'une procédure
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
En programmation impérative, un programme est une suite d’instructions qui font
|
||
évoluer l’état mémoire, le résultat est dans l’état final de la mémoire.
|
||
|
||
- une procédure peut prendre des paramètres
|
||
- elle modifie l'état courant du système
|
||
|
||
- Déclaration des paramètes
|
||
- Déclaration du corps
|
||
- Appel de la procédure
|
||
|
||
.. raw:: latex
|
||
|
||
\begin{algorithm}
|
||
\caption{Procédure de permutation de deux entiers}\label{permutation}
|
||
\begin{algorithmic}[1]
|
||
\Procedure{permuter}{$a,b$}{}
|
||
\BState \emph{parametres}:
|
||
\State $a: \textit{int}$
|
||
\State $b: \textit{int}$
|
||
\BState \emph{locales}:
|
||
\State $z: \textit{int}$ \Comment{Une variable intermédiaire est nécessaire}
|
||
\BState \emph{corps}:
|
||
\State $z \gets a$
|
||
\State $a \gets b$
|
||
\State $b \gets z$
|
||
\EndProcedure
|
||
\State \Call{permuter}{10, 12} \Comment{appel de la procédure}
|
||
\end{algorithmic}
|
||
\end{algorithm}
|
||
|
||
effet de bord
|
||
|
||
toute modification de la mémoire ou modification d'un support externe
|
||
|
||
instruction
|
||
|
||
commande ou phrase en mesure de modifier l'état du programme ou de la machine hôte
|
||
(allocation mémoire, support externe, disque, écran...)
|
||
|
||
Une procédure ne renvoie pas de valeur, mais provoque un 'effet de bord' (écriture dans une globale, dans un flux sortant etc.).
|
||
|
||
Une procédure permet de créer une instruction nouvelle qui deviendra une primitive pour le programmeur.
|
||
Cela permet de structurer le texte source du programme et améliorer sa
|
||
lisibilité. Cela permet aussi d'appeler plusieurs fois, et à plusieurs endroit
|
||
dans le code, cette primitive.
|
||
|
||
Appel d'une procédure
|
||
~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
(ex: pseudo-pascal)
|
||
|
||
**déclaration de procédure**
|
||
|
||
.. raw:: latex
|
||
|
||
\begin{algorithm}
|
||
\caption{Procédure de permutation de deux entiers}\label{appelpermutation}
|
||
\begin{algorithmic}[1]
|
||
\Procedure{permuter}{$a,b$}{}
|
||
\BState \emph{parametres}:
|
||
\State $a: \textit{int}$ \Comment{paramètres formels de la procédure}
|
||
\State $b: \textit{int}$
|
||
\BState \emph{locales}:
|
||
\State $z: \textit{int}$ \Comment{les variables locales de la procédure}
|
||
\BState \emph{corps}:
|
||
\State ... \Comment{Le corps de la procedure}
|
||
\EndProcedure
|
||
\State \Call{permuter}{10, 12} \Comment{l'appel de la procédure}
|
||
\end{algorithmic}
|
||
\end{algorithm}
|
||
|
||
|
||
- les variables x1,...,xn sont appelées *paramètres formels* de p
|
||
- les variables v1,...,vm sont appelées *les variables locales* de p
|
||
|
||
les valeurs effectivement passées en paramètres, ici `10, 12`
|
||
sont appelées **paramètres effectifs** de p
|
||
|
||
signature
|
||
|
||
C'est l'ensemble paramètre formel + resultat de l'appel
|
||
|
||
fermeture
|
||
|
||
L'ensemble procédure + variables locales + signature + parametres effectifs
|
||
est appelé une **fermeture**. C'est la procédure + son contexte qui permet
|
||
de l'instancier dans un programme.
|
||
|
||
Environnement
|
||
|
||
Contexte d’évaluation d'une expression ou d'une fonction
|
||
|
||
Portée
|
||
|
||
La portée d'un identifiant (une variable) est sa condition d'utilisation dans un contexte donné
|
||
(utilisation locale uniquement, ou bien globale, ou bien locale et globale)
|
||
La portée d’une liaison est la portion du code dans laquelle cette
|
||
liaison est valide (i.e. où un identifiant est lié à une expression).
|
||
|
||
.. ifconfig:: exercice
|
||
|
||
**Exercice** : Que donne ce code ?
|
||
|
||
.. code-block:: ocaml
|
||
|
||
# let x = 42 in
|
||
let y = x - 1 in x - y ;;
|
||
|
||
.. ifconfig:: correction
|
||
|
||
**Correction** :
|
||
|
||
.. code-block:: ocaml
|
||
|
||
- : int = 1
|
||
|
||
|
||
.. code-block:: ocaml
|
||
|
||
let a = 3 (* première liaison pour l'identifiant a *)
|
||
let b = 5 and c = 6
|
||
let somme = a + b + c
|
||
val somme : int = 14
|
||
let a = 45 (* deuxième liaison pour l'identifiant a *)
|
||
somme
|
||
val a : int = 45
|
||
|
||
.. ifconfig:: exercice
|
||
|
||
**Exercice** : Que donne ce code ?
|
||
|
||
.. code-block:: ocaml
|
||
|
||
let a = 3 and b = 4 and c = 8 ;;
|
||
let somme = a + b + c ;;
|
||
val somme : int = ???
|
||
let a = 44
|
||
let b = 5
|
||
let c = 1
|
||
somme
|
||
- : int = ???
|
||
|
||
.. ifconfig:: correction
|
||
|
||
.. code-block:: ocaml
|
||
|
||
let a = 3 and b = 4 and c = 8 ;;
|
||
- : int = 15
|
||
let somme = a + b + c ;;
|
||
val somme : int = 15
|
||
let a = 44
|
||
let b = 5
|
||
let c = 1
|
||
somme
|
||
- : int = 15
|
||
|
||
Même code en python
|
||
|
||
.. code-block:: python
|
||
|
||
>>> a = 1
|
||
>>> b = 2
|
||
>>> c = 3
|
||
>>> somme = a + b + c
|
||
>>> somme
|
||
6
|
||
>>> a = 56
|
||
>>> b = 5678
|
||
>>> c = 56789
|
||
>>> somme
|
||
6
|
||
>>>
|
||
|
||
Portée locale dans une expression
|
||
|
||
.. code-block:: ocaml
|
||
|
||
# let a = 2 and b = 3 and c = 4 in
|
||
let somme = a+b+c in
|
||
somme
|
||
- : int = 9
|
||
# somme ;;
|
||
Error: Unbound value somme
|
||
# a ;;
|
||
Error: Unbound value a
|
||
|
||
.. important::
|
||
|
||
L’ordre d’évaluation dans un let ... in ... est bien déterminé,
|
||
sans grande importance dans un cadre purement fonctionnel, mais important
|
||
en cas d’effets de bord
|
||
|
||
|
||
Exemple de variable globale modifiée localement (**attention, mauvaise pratique** !) :
|
||
|
||
.. code-block:: python
|
||
|
||
>>> a = 5
|
||
>>> def print_a():
|
||
... print("La variable a = {0}.".format(a))
|
||
...
|
||
>>> print_a()
|
||
La variable a = 5.
|
||
>>> a = 8
|
||
>>> print_a()
|
||
La variable a = 8.
|
||
>>>
|
||
|
||
|
||
|
||
niveau
|
||
|
||
Le niveau d’une déclaration (de variable ou de procédure) est le nombre
|
||
de procédures sous lesquelles elle est déclarée. Le programme principal
|
||
a le niveau 0.
|
||
|
||
.. code-block:: python
|
||
:linenos:
|
||
|
||
def _get_config(name):
|
||
# return config value
|
||
if not isfile(CONFIG_FILE):
|
||
raise Exception("Fichier de configuration non existant")
|
||
from ConfigParser import ConfigParser
|
||
cfg = ConfigParser(allow_no_value=True)
|
||
cfg.read(CONFIG_FILE)
|
||
|
||
if name == "SUBNETS":
|
||
return eval(cfg.get('eole', 'subnets')) # c'est une liste de tuple # FIXME
|
||
elif name == "LEASES_FILE":
|
||
DHCP_PATH = cfg.get('eole', 'container_path_dhcp')
|
||
return join('/', DHCP_PATH, 'var/lib/dhcp/dhcpd.leases')
|
||
def get_routes(*args, **kwargs):
|
||
"""
|
||
Send list of reserved IP
|
||
return list of tuple (id, machine name, IP, MAC Adress)
|
||
"""
|
||
cfg = creole_loader(load_extra=True, rw=False, owner=MODNAME,
|
||
mandatory_permissive=False)
|
||
return zip(cfg.dhcp.dhcp.id_dhcp.id_dhcp, cfg.dhcp.dhcp.id_dhcp.hostname,
|
||
cfg.dhcp.dhcp.id_dhcp.ip, cfg.dhcp.dhcp.id_dhcp.macaddress)
|
||
|
||
On voit que l'objet `cfg` ligne 6 et 7 a le même nom que l'objet `cfg` ligne 19.
|
||
C'est autorisé et les espaces de nommages sont différents.
|
||
|
||
|
||
Description d'une fonction
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Une fonction renvoie une valeur et ne modifie pas l'état courant du programme
|
||
en cours d'exécution ni ne réalise d'effets de bord. Elle renvoie
|
||
**toujours** quelque chose (même la valeur ``None`` qui n'est pas rien)
|
||
|
||
- une procédure peut prendre des paramètres
|
||
- elle modifie l'état courant du système
|
||
|
||
- Déclaration des paramètes
|
||
- Déclaration du corps
|
||
- Appel de la fonction
|
||
|
||
En programmation fonctionnelle, programme est un ensemble de définitions de fonctions,
|
||
un résultat est l'application d’une fonction à une structure de données effective.
|
||
|
||
- composant de base : la fonction
|
||
- opération de base : l’application
|
||
|
||
.. raw:: latex
|
||
|
||
\begin{algorithm}
|
||
\caption{Exemple de fonction}\label{fonction}
|
||
\begin{algorithmic}[1]
|
||
\Function{permuter}{$a,b$}{} \Comment{définition de la fonction}
|
||
\BState \emph{parametres}: \Comment{déclaration (noms, types) des paramètres formels}
|
||
\State $a: \textit{int}$
|
||
\State $b: \textit{int}$
|
||
\BState \emph{locales}: \Comment{déclaration (noms, types) des valeurs locales}
|
||
\State $z: \textit{int}$
|
||
\BState \emph{corps}:
|
||
\State $z \gets a$
|
||
\State $a \gets b$
|
||
\State $b \gets z$
|
||
\BState \emph{return}: \Comment{La valeur, le résulat renvoyé par la fonction}
|
||
\EndFunction
|
||
\State \Call{permuter}{10, 12} \Comment{appel de la fonction}
|
||
\BState \emph{result}:
|
||
\State (12, 10) \Comment{Le résultat effectif de la fonction après appel}
|
||
\end{algorithmic}
|
||
\end{algorithm}
|
||
|
||
.. ifconfig:: exercice
|
||
|
||
**Exercice** : factoriser le code suivant
|
||
::
|
||
|
||
Ecrire "Etes-vous marié ?"
|
||
Rep1 <- ""
|
||
TantQue Rep1 <> "Oui" et Rep1 <> "Non"
|
||
Ecrire "Tapez Oui ou Non"
|
||
Lire Rep1
|
||
FinTantQue
|
||
...
|
||
Ecrire "Avez-vous des enfants ?"
|
||
Rep2 <- ""
|
||
TantQue Rep2 <> "Oui" et Rep2 <> "Non"
|
||
Ecrire "Tapez Oui ou Non"
|
||
Lire Rep2
|
||
FinTantQue
|
||
|
||
.. ifconfig:: correction
|
||
|
||
**Correction** :
|
||
|
||
::
|
||
Fonction RepOuiNon() en caractère
|
||
Truc <- ""
|
||
TantQue Truc <> "Oui" et Truc <> "Non"
|
||
Ecrire "Tapez Oui ou Non"
|
||
Lire Truc
|
||
FinTantQue
|
||
Renvoyer Truc
|
||
Fin
|
||
|
||
Ecrire "Etes-vous marié ?"
|
||
Rep1 <- RepOuiNon()
|
||
...
|
||
Ecrire "Avez-vous des enfants ?"
|
||
Rep2 <- RepOuiNon()
|
||
|
||
|
||
Définition mathématique
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
fonction
|
||
|
||
Une fonction f d’un ensemble E vers un ensemble F est une
|
||
correspondance qui associe à chaque élément de E au plus
|
||
un élément de F.
|
||
|
||
- E est appelé le domaine de définition
|
||
- F est appelé codomaine
|
||
- la **signature** de la fonction : `E → F (int -> int = <fun>)`
|
||
|
||
|
||
Exemple de signature d'une fonction
|
||
|
||
::
|
||
|
||
Fonction RepOuiNon(Msg en Caractère) en Caractère
|
||
Ecrire Msg
|
||
Truc <- ""
|
||
TantQue Truc <> "Oui" et Truc <> "Non"
|
||
Ecrire "Tapez Oui ou Non"
|
||
Lire Truc
|
||
FinTantQue
|
||
Renvoyer Truc
|
||
Fin Fonction
|
||
|
||
...
|
||
Rep1 <- RepOuiNon("Etes-vous marié ?")
|
||
...
|
||
Rep2 <- RepOuiNon("Avez-vous des enfants ?")
|
||
...
|
||
|
||
|
||
curryfication
|
||
|
||
évaluation de l'application d'une fonction
|
||
|
||
- évaluter `(f x y)`
|
||
- peut donner une **valeur fonctionnelle**
|
||
- évaluation de la valeur fonctionnelle sur une valeur des types de base
|
||
|
||
::
|
||
|
||
let g = function n -> (function p -> p + 1) n;;
|
||
|
||
Typage d'une fonction
|
||
~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
.. code-block:: ocaml
|
||
|
||
# let f x y z = if x > 0 then y + x else z - x;;
|
||
val f : int -> int -> int -> int = <fun>
|
||
|
||
c’est en fait une fonction à un argument qui retourne une fonction::
|
||
|
||
.. code-block:: ocaml
|
||
|
||
val f : int -> (int -> (int -> int)) = <fun>
|
||
|
||
application de f à trois valeurs
|
||
|
||
.. code-block:: ocaml
|
||
|
||
# f 1 2 3;;
|
||
- : int = 3
|
||
|
||
en programmation fonctionnelle,
|
||
les fonctions sont des valeurs comme les autres
|
||
|
||
|
||
.. code-block:: ocaml
|
||
|
||
# fun x -> x * x;;
|
||
- : int -> int = <fun>
|
||
|
||
Récursivité
|
||
~~~~~~~~~~~~
|
||
|
||
.. code-block:: ocaml
|
||
|
||
let rec fact n =
|
||
if n=0 then 1 else n * fact (n-1)
|
||
|
||
|
||
équivalent impératif utilisant une boucle
|
||
|
||
.. code-block:: c
|
||
|
||
int fact(int n){
|
||
int f = 1 ;
|
||
int i = n ;
|
||
while (i>0){
|
||
f = f * i;
|
||
i-- ;
|
||
} ;
|
||
return f ;
|
||
}
|
||
|
||
Définitions par cas
|
||
~~~~~~~~~~~~~~~~~~~
|
||
|
||
.. code-block:: ocaml
|
||
|
||
let rec fact n =
|
||
match n with
|
||
0 -> 1
|
||
| -> n * fact (n-1)
|
||
|
||
**exemple** : la fonction puissance
|
||
|
||
.. code-block:: ocaml
|
||
|
||
let rec puissance x n = match n with
|
||
0 -> 1
|
||
| -> x * puissance x (n-1)
|
||
|
||
|
||
|
||
.. ifconfig:: exercice
|
||
|
||
**Portée locale dans une fonction**
|
||
Quelles sera la valeur de la variable `a` ?
|
||
|
||
.. code-block:: python
|
||
|
||
>>> a = 1
|
||
>>> def myfunc():
|
||
... a = 2
|
||
... return a + 1
|
||
...
|
||
>>> a = myfunc() + a
|
||
|
||
|
||
.. ifconfig:: correction
|
||
|
||
Correction:
|
||
|
||
.. code-block:: python
|
||
|
||
>>> a = 1
|
||
>>> def myfunc():
|
||
... a = 2
|
||
... return a + 1
|
||
...
|
||
>>> a = myfunc() + a
|
||
>>> a
|
||
4
|
||
>>>
|
||
|
||
|
||
.. ifconfig:: exercice
|
||
|
||
**Exercice** : Portée locale dans une fonction avec variable globale
|
||
Quelles sera la valeur de la variable `a` ?
|
||
|
||
.. code-block:: python
|
||
|
||
>>> a = 1
|
||
>>> def myfunc():
|
||
... global a
|
||
... a = 2
|
||
... return a + 1
|
||
...
|
||
>>> a = myfunc() + 3
|
||
>>>
|
||
|
||
.. ifconfig:: correction
|
||
|
||
**Correction** :
|
||
|
||
.. code-block:: python
|
||
|
||
>>> a = 1
|
||
>>> def myfunc():
|
||
... global a
|
||
... a = 2
|
||
... return a + 1
|
||
...
|
||
>>> myfunc()
|
||
3
|
||
>>> a
|
||
2
|
||
>>> a = myfunc() + 3
|
||
>>> a
|
||
6
|
||
>>>
|