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