Les fonctions

publicité
Programmation Fonctionnelle
Stefano Guerrini
[email protected]
http://www.lipn.univ-paris13.fr/~guerrini
Les fonctions
LIPN - Institut Galilée, Université Paris Nord 13
Licence Info 3
février 2013
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
Les fonctions
février 2013
1 / 131
S. Guerrini (LIPN - Paris 13)
Fonctions n-aires
Programmation Fonctionnelle
Les fonctions
Fonction n-aire ou sur n-uplets
février 2013
25 / 131
Fonctions n-aires
Exemples
On peut toujours transformer une fonction binaire en une fonction qui prend
ses deux arguments comme éléments d’une couple :
# let curry f (x, y) = f x y;;
val curry : (’a -> ’b -> ’c) -> ’a * ’b -> ’c = <fun>
Une fonction binaire
# let f x y = x + y;;
val f : int -> int -> int = <fun>
en e↵et, c’est une fonction unaire qui renvoie une fonction unaire
On peut définir une fonction binaire en utilisant un couple
# let f1 x y = x + y;;
val f1 : int -> int -> int = <fun>
# let g (x,y) = x + y;;
val g : int * int -> int = <fun>
# f1 3 4;;
- : int = 7
en e↵et, cette fonction est une fonction unaire sur une couple
La définition précédente est équivalente à
# let f2 = curry f1;;
val f2 : int * int -> int = <fun>
# f2 (3,4);;
- : int = 7
# f3 3 4;;
- : int = 7
On peux aussi bien définir la fonction inverse de curry
# let g p = fst p + snd p;;
val g : int * int -> int = <fun>
# let uncurry f x y = f (x,y);;
val uncurry : (’a * ’b -> ’c) -> ’a -> ’b -> ’c = <fun>
# let f3 = uncurry f2;;
val f3 : int -> int -> int = <fun>
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
février 2013
26 / 131
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
février 2013
27 / 131
Les fonctions
La récursion
Les fonctions
Fonctions récursives
La récursion
Fibonacci
La fonction de Fibonacci
Une définition de fonction peut être récursive, mais il faut ajouter le mot clé
rec à let
# let rec fib n = if n < 2 then 1 else (fib (n-1) + fib (n-2));;
val fib : int -> int = <fun>
# let rec exp x =
if (x = 0) then 1
else if (x mod 2 = 1) then (exp (x/2)) * (exp (x/2)) * 2
else (exp (x/2)) * (exp (x/2));;
val exp : int -> int = <fun>
La précédente fonction n’est pas trop efficace : le problème est qu’il y a une
double appel de la fonction récursive.
Essayez de calculer fib 40;;
La définition précédente est très inefficace car elle évalue deux fois la même
expression récursive dans chaque branche du cas x > 0.
Pour éviter ce type de problèmes on peut définir des noms locaux
# let rec exp x =
if (x = 0) then 1
else let h = exp (x/2) in
if (x mod 2 = 1) then h * h * 2
else h * h;;
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
Les fonctions
février 2013
28 / 131
3
4
Programmation Fonctionnelle
février 2013
29 / 131
Les déclarations
Déclarations globales et déclarations locales
un entier n,
une valeur f0 de type ↵
(par exemple : un entier),
une fonction f1 : ↵ ! ↵
(par exemple : une fonction sur les entiers qui double son argument)
et calcul f1n (f0 )
(qu’est-ce qu’on obtient avec les exemples précédentes ?)
let rec iter n f0 f1 =
if n = 0
then f0
else f1 (iter (n-1) f0 f1);;
let fibstep (x,y) = (x+y, x);;
# let x = 3 * 4;;
val x : int = 12
En réponse à une déclaration globale, la boucle affiche l’e↵et que la
déclaration a eu sur l’environnement globale (en ajoute au type et à la valeur
de l’expression).
Dans l’exemple, val x indique que
2
le nom x a été ajouté à l’environnement ;
que le nom x est associé à une valeur.
Dans une déclaration locale
let x = 3 in x + 2;;
le nom déclaré n’est connu que dans l’expression qui suive le in.
Une déclaration locale peut redéfinir localement un nom global
let dup x = 2 * x;;
let exp2 n =
fst (iter n 1 dup);;
# let x
val x :
# let x
- : int
# x;;
- : int
let fibonacci n =
fst (iter n (1,0) fibstep);;
Programmation Fonctionnelle
Une déclaration globale associe un nom à une valeur
1
On peut aussi bien utiliser iter pour définir fibonacci :
S. Guerrini (LIPN - Paris 13)
S. Guerrini (LIPN - Paris 13)
Les fonctions
On n’a pas encore de commandes pour l’itération (while ou repeat).
Mais la possibilité de définir des fonctions d’ordre supérieure (fonctions qui
prennent comme arguments des fonctions et revoient des fonctions comme
résultat) nous permet de définir une fonctionnelle iter qui prend
2
let fibonacci n =
let rec fib n =
if n = 0
then (1,0)
else let p = fib (n-1)
in let x = fst p
and y = snd p
in (x+y, x)
in fst (fib n);;
let rec fib n =
if n = 0
then (1,0)
else let p = fib (n-1)
in let x = fst p
and y = snd p
in (x+y, x);;
La récursion
Fonctions itératives
1
On peut pourtant coder de façon “récursive” la version “itérative” de
Fibonacci qui calcul au meme temps fib n;; et fib (n-1);;
février 2013
30 / 131
= 2;;
int = 2
= 3 in x;;
= 3
= 2
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
février 2013
31 / 131
Les fonctions
Les déclarations
Les fonctions
Déclarations et expressions
Noms OCaml vs. variables C
Une déclaration locale est une expression et on peut l’utiliser comme une
autre expression quelconque du même type
Les noms OCaml et les variables C sont des objets de natures complètement
di↵érentes.
# 4 * let x = 2 in x + 4;;
- : int = 24
I
Une déclaration globale n’est pas une expression
I
# 4 * let x = 2;;
Characters 14-16:
4 * let x = 2;;
^^
Error: Syntax error
I
I
Une déclaration globale peut être utiliser seulement dans des contextes
particuliers, par exemple, comme commande pour la boucle.
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
Les fonctions
Les déclarations
février 2013
En OCaml, une variable est un nom pour une valeur, alors qu’en C une
variable est un nom pour une case mémoire.
En OCaml, il est donc hors de question de pouvoir changer la valeur d’une
variable.
La déclaration globale (ou locale, voir plus loin) d’OCaml, n’a rien à voir avec
l’a↵ectation du C.
Pour obtenir des variables OCaml avec un comportement similaire aux
variables C il faut utiliser
F
F
32 / 131
des données d’un type particulier, les enregistrements à champs modifiables,
et les références (ou pointers).
S. Guerrini (LIPN - Paris 13)
Déclarations simultanées et recursion mutuelle
Programmation Fonctionnelle
Les fonctions
Déclarations simultanées
février 2013
33 / 131
Déclarations simultanées et recursion mutuelle
La récursion mutuelle
Dans une déclaration globale ou locale on peut définir plusieurs variable au
même temps et en parallèle dans une déclaration simultanée.
# let x
val x :
val y :
# let x
- : int
= 3 and y = 4;;
int = 3
int = 4
= y and y = x in (x,y);;
* int = (4, 3)
Les déclarations récursives simultanées permettent de définir des fonctions
mutuellement récursives
Dans une déclaration simultanée les expressions sont évaluées avant d’associer
les noms de la déclaration aux valeurs des expressions correspondantes.
Une déclaration simultanée n’est pas équivalent à une chaı̂ne de déclaration
#
#
-
let x
: int
let y
: int
=
*
=
*
y in let y = x in (x,y);;
int = (4, 4)
x in let x = y in (x,y);;
int = (3, 3)
# let rec isOdd x = if (x = 0) then false else isEven (x-1)
and isEven x = if (x = 0) then true else isOdd (x-1);;
val isOdd : int -> bool = <fun>
val isEven : int -> bool = <fun>
# let
and g
val
val g
rec f x = if x <= 1 then 1 else g (x+2)
x = f (x-3) + 4;;
f : int -> int = <fun>
: int -> int = <fun>
Dans une chaı̂ne de déclarations l’ordre est relevant, tandis que dans une
déclaration simultanée l’ordre des déclarations n’a aucun influence sur le
résultat
# let y = x and x = y in (x,y);;
- : int * int = (4, 3)
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
février 2013
34 / 131
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
février 2013
35 / 131
Téléchargement