formations/algorithmique/cours/fonctions.txt

533 lines
12 KiB
Plaintext
Raw Normal View History

2017-04-13 10:50:18 +02:00
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 dinstructions 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}
2017-04-13 16:08:30 +02:00
\caption{Procédure de permutation de deux entiers}\label{appelpermutation}
2017-04-13 10:50:18 +02:00
\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
2017-04-13 17:36:25 +02:00
La portée d'un identifiant (une variable) est sa condition d'utilisation dans un contexte donné
2017-04-13 10:50:18 +02:00
(utilisation locale uniquement, ou bien globale, ou bien locale et globale)
2017-04-13 17:36:25 +02:00
La portée dune liaison est la portion du code dans laquelle cette
liaison est valide (i.e. où un identifiant est lié à une expression).
2017-04-13 10:50:18 +02:00
.. 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
2017-04-13 17:36:25 +02:00
.. 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
2017-04-18 21:53:35 +02:00
.. important::
2017-04-13 17:36:25 +02:00
2017-04-18 21:53:35 +02:00
Lordre dévaluation dans un let ... in ... est bien déterminé,
sans grande importance dans un cadre purement fonctionnel, mais important
en cas deffets de bord
2017-04-13 17:36:25 +02:00
2017-04-18 21:53:35 +02:00
Exemple de variable globale modifiée localement (**attention, mauvaise pratique** !) :
2017-04-13 10:50:18 +02:00
.. 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 dune 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.
2017-04-18 21:53:35 +02:00
.. 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.
2017-04-13 10:50:18 +02:00
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 dune fonction à une structure de données effective.
- composant de base : la fonction
- opération de base : lapplication
.. 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()
2017-04-13 10:50:18 +02:00
Définition mathématique
~~~~~~~~~~~~~~~~~~~~~~~~~
fonction
Une fonction f dun 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 ?")
...
2017-04-13 10:50:18 +02:00
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;;
2017-04-13 17:08:27 +02:00
Typage d'une fonction
2017-04-13 10:50:18 +02:00
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: ocaml
# let f x y z = if x > 0 then y + x else z - x;;
val f : int -> int -> int -> int = <fun>
cest 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>
2017-04-13 17:08:27 +02:00
Récursivité
2017-04-13 10:50:18 +02:00
~~~~~~~~~~~~
.. 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)
2017-04-26 11:48:32 +02:00
.. 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
>>>