Programmation fonctionnelle

publicité
Programme 5 : Tracé de courbe
Programmation fonctionnelle
Notions introduites :
Notes de cours
I
n-uplets, tableaux
I
ordre d’évaluation
fonctions de première classe, ordre supérieur
fonctions anonymes
filtrage
I
I
I
Cours 2
4
20
15
2
2
30
10
10
15
18 Septembre 2013
Sylvain Conchon
[email protected]
1/19
12/19
Les n-uplets
Les n-uplets
Expressions de type produit
#
#
#
#
#
I
I
I
3/19
2
Attention à ne pas confondre les types des expressions suivantes :
(1, ’a’) ;;
: int * char = (1, ’a’)
(1 + 2, ’a’, true && false) ;;
: int * char * bool = (3, ’a’, false)
("hello", (1, 3.4)) ;;
: string * (int * float) = ("hello", (1, 3.4))
fst (1, ’a’) ;;
: int = 1
snd ((1, ’a’), (2+3, true, "a")) ;;
: int * bool * string = (5, true, "a")
#
#
#
-
(1, 2, 3) ;;
: int * int * int = (1, 2, 3)
((1, 2), 3) ;;
: (int * int) * int = ((1, 2), 3)
(1, (2, 3)) ;;
: int * (int * int) = (1, (2, 3))
I
int * int * int désigne un triplet d’entiers
I
(int * int) * int est une paire dont la première
composante est une paire d’entiers et la deuxième un entier
le type produit * représente les n-uplets
les n-uplets peuvent être arbitrairement imbriqués
fst et snd permettent d’accéder respectivement à la première
et à la deuxième composante d’une valeur de type τ1 * τ2
I
int * (int * int) est une paire dont la première
composante est un entier et la deuxième une paire d’entiers
34/19
4
Les n-uplets
Les tableaux
# let t = [| 5; 1; 3 |] ;;
val t : int array = [| 5; 1; 3 |]
# t.(0) <- 10 ;;
- : unit = ()
# t.(0) ;;
- : int = 10
# Array.length t ;;
- : int = 3
Attention également à ne pas confondre les types des fonctions
suivantes :
# let
val f
# let
val f
I
I
f
:
f
:
x y
int
(x,
int
z = x + y + z ;;
-> int -> int -> int = <fun>
y, z) = x + y + z ;;
* int * int -> int = <fun>
I
int -> int -> int -> int est le type d’une fonction avec
trois arguments de type int
I
I
(int * int * int) -> int désigne le type d’une fonction
à un argument (un triplet) de type int * int * int
5/19
array est le type “générique” des tableaux
t.(i) est l’accès au ième élément d’un tableau t
t.(i) <- v affecte la valeur v dans ième case de t
L’indexation des cases d’un tableau contenant
n éléments se fait de 0 à n − 1
56/19
Les tableaux
# let
val t
# let
val t
6
Les tableaux multi-dimensionnels
t
:
t
:
# let t = [| [|1; 0|]; [|0; 1|] |] ;;
val t : int array array = [| [|1; 0|]; [|0; 1|] |]
# t.(0).(1) ;;
- : int = 0
# t.(1) ;;
- : int array = [|0; 1|]
# let t = Array.make matrix 2 2 0. ;;
val t : float array array = [|[|0.; 0.|]; [|0.; 0.|]|]
= Array.make 4 ’a’ ;;
char array = [| ’a’; ’a’; ’a’; ’a’ |]
= Array.init 5 (fun i -> 2 * i) ;;
int array = [|0; 2; 4; 6; 8|]
I
Array.make n v crée un tableau de n cases, toutes
initialisées avec la même valeur v (attention au partage) ;
I
Array.init n f crée un tableau de taille n initialisé par la
fonction f : pour chaque case d’indice i, f i renvoie la valeur
à stocker en i.
I
I
I
les tableaux multi-dimensionnels sont simplement
représentés par des tableaux contenant d’autres tableaux
les matrices sont donc de type α array array, où α
représente n’importe quel type
l’opération .(i) est associative à gauche, c’est-à-dire qu’il
faut lire t.(i).(j).(k) comme t.(i) .(j) .(k)
7/19
78/19
8
Les fonctions anonymes
#
#
#
-
L’ordre supérieur
fun x -> x * x ;;
: int -> int = <fun>
(fun x -> x * x) 4 ;;
: int = 16
fun x y -> x * y ;;
: int -> int -> int = <fun>
I
le mot-clé fun permet de créer des valeurs fonctionnelles
I
les fonctions anonymes s’appliquent comme les fonctions
nommées
I
la déclation let f x y = x * y est donc équivalente à
Les fonctions sont des valeurs comme les autres
Elles peuvent être
I
stockées dans une structure de donnée
I
passées en argument à une autre fonction
I
retournées comme résultat d’une fonction
Les fonctions prenant des fonctions en arguments ou rendant des
fonctions en résultat sont dites d’ordre supérieur
let f = fun x y -> x * y
9/19
910/19
Fonctions stockées dans des structures de données
Fonctions comme arguments
# ( (fun x -> x + 1), 4.2 ) ;;
- : (int -> int) * float = (<fun>, 4)
# let t = [| (fun x -> x * 2); (fun x -> x + 1) |] ;;
val t : (int -> int) array = [| <fun>; <fun> |]
# t.(0) 5 ;;
- : int = 10
# let f = ref (fun x -> x + 2) ;;
val f : (int -> int) ref
# !f 5 ;;
- : int = 7
# f : = (fun x -> x / 4) ;;
- : unit = ()
I
11/19
10
I
certaines fonctions prennent naturellement des fonctions en
arguments
I
par exemple, les notations mathématiques telles que la
sommation Σni=1 f (i) se traduisent immédiatement si l’on peut
utiliser des arguments fonctionnels
# let rec
if n<=0
else (f
val somme
# somme (
- : int =
les fonctions peuvent être stockées comme des valeurs
quelconques
11
12/19
somme (f, n) =
then 0
n) + somme (f, n - 1) ;;
: (int -> int) * int -> int = <fun>
(fun x -> x * x), 10) ;;
385
12
Exemple : la méthode dichotomique
(1/2)
Exemple : la méthode dichotomique
# let rec dichotomie f (a, b) epsilon =
if abs float (b -. a) < epsilon then a
else
let c = (a +. b) /. 2.0 in
let bornes =
if (f a) *. (f c) > 0. then (c, b) else (a, c)
in
dichotomie f bornes epsilon
Si f est une fonction continue et monotone, on peut trouver un
zéro de f sur un intervalle [a, b] par la méthode dichotomique
quand f (a) et f (b) sont de signes opposés :
I
I
(2/2)
si est la précision souhaitée et que |b − a| < alors on
renvoie a
val dichotomie :
sinon, couper l’intervalle [a, b] en deux et recommencer sur
l’intervalle contenant 0
(float -> float) -> (float * float) -> float -> float = <fun>
I
on peut utiliser cette méthode pour trouver un encadrement
de π en le calculant comme zéro de la fonction cos(x/2)
# dichotomie (fun x->cos (x/.2.0)) (3.1, 3.2) 1e-10 ;;
- : float = 3.14159265356138384
13/19
13
14/19
Fonctions comme résultats
I
14
Applications partielles
# let plus2 = plus 2 ;;
val plus2 : int -> int = <fun>
# plus2 10 ;;
- : int = 12
# plus2 100 ;;
- : int = 102
Les fonctions à plusieurs arguments sont en fait des fonctions
d’ordre supérieur qui rendent des fonctions comme résultats
# let plus x y = x + y ;;
val plus : int -> int -> int
I
I
Il faut lire le type de cette fonction de la manière suivante
int -> (int -> int)
I
I
De manière équivalente, on peut écrire la fonction plus de la
façon suivante afin de souligner son résultat fonctionnel
# let f x =
Printf.printf "x = %d" x;
(fun y -> Printf.printf "y = %d" y);;
# let f1 = f 1 ;;
x = 1 val f1 : int -> unit = <fun>
# let () = f1 2 ;;
y = 2 - : unit = ()
# let plus x = (fun y -> x+y) ;;
val plus : int -> int -> int
15/19
Les fonctions d’ordre supérieur rendant des fonctions en
résultats peuvent être appliquées partiellement
On peut faire des calculs avant de renvoyer un résultat
15
16/19
16
Fermetures
Exemple de fonctions en arguments et en résultat (1/2)
# let compteur i =
let etat = ref i in
fun () -> etat := !etat + 1; !etat ;;
val compteur : int -> unit -> int
# let f = compteur 0 ;;
val f : unit -> int = <fun>
# f () ;;
- : int = 1
# f () ;;
- : int = 2
# let g = compteur 10 ;;
val g : unit -> int = <fun>
# g() ;;
- : int = 11
I
I
On peut calculer de façon approximative la dérivée f 0 d’une
fonction f avec un petit intervalle dx de la manière suivante :
# let derive (f, dx) =
fun x -> ( f(x +. dx) -. f(x) ) /. dx ;;
val derive :
(float -> float) * float -> float -> float = <fun>
# derive ( (fun x -> x *. x), 1e-10) 1. ;;
- : float = 2.000000165480742
Une fermeture est une fonction avec un état interne
Dans cet exemple, les fonctions f et g ont chacune leur état
18/19
17
17/19
Exemple : fonctions en arguments et en résultat
I
(2/2)
On peut réécrire la fonction derive de la manière suivante
# let derive dx f =
fun x -> ( f(x +. dx) -. f(x) ) /. dx ;;
val derive :
float -> (float -> float) -> float -> float
I
On fixe le paramètre dx par application partielle
# let derivation = derive 1e-10 ;;
val derivation : (float -> float) -> float -> float
I
19/19
On peut alors définir par exemple la dérivée de la fonction
sinus simplement de la manière suivante :
# let sin’ = derivation sin ;;
val sin’ : float -> float
# sin’ 1. ;;
- : float = 0.540302247387103307
# cos 1. ;;
- : float = 0.540302305868139765
19
18
Téléchargement