Cours no 2 : Itération. Du impératif au style fonctionnel 1
Transcription
Cours no 2 : Itération. Du impératif au style fonctionnel 1
Univ. Lille1 - Licence info 3ème année 2012-2013 Expression Logique et Fonctionnelle . . . Évidemment o Cours n 2 : Itération. Du impératif au style fonctionnel 1 Itérateurs sur les listes 1.1 map La fonction List.map est un itérateur sur les listes. Elle est du type (0 a → 0 b) → 0 a list → 0 b list et, appliquée à une fonction f et une liste [a1 ; a2 ; . . . ; an ] renvoie la liste [f (a1 ); f (a2 ); . . . ; f (an )]. En voici deux exemples d'utilisation. # # - List.map (fun x -> 2*x) [0; 1; 2; 3; 4] ;; : int list = [0; 2; 4; 6; 8] List.map char_of_int [69; 76; 70; 69] ;; : char list = [’E’; ’L’; ’F’; ’E’] En voici une réalisation récursive (non terminale) en Caml à l'aide d'un ltrage de motif. let rec map = fun f l -> match l with | x::l’ -> (f x)::(map f l’) | _ -> [] 1.2 fold_left let rec fold_left = fun f x l -> match l with | y::l’ -> fold_left f (f x y) l’ | _ -> x 2 2.1 Passer de l'impératif au fonctionnel État Un programme impératif agit sur son état en modiant la valeur des variables qui le constituent. Un état peut être représenté en programmation fonctionnelle par un n-uplet des valeurs des variables. Une instruction qui transforme un état (comme par exemple une aectation) peut alors être considérée comme une fonction qui à un n-uplet des valeurs des variables, associe un nouvel n-uplet. 2.2 Séquence Voici une fonction écrite en C qui calcule la puissance 15ème de l'entier qu'on lui passe en paramètre. L'algorithme suivi permet ce calcul en eectuant cinq multiplications uniquement (ce qui correspond au nombre minimal de multiplications permettant un tel calcul). Le calcul est réalisé en utilisant trois variables locales b, c et d. int puissance15(int a) { int b,c,d ; // b = ??, c = ??, d = ?? b = a *a*a ; // b = a^3, c = ??, d = ?? c = b *b ; // b = a^3, c = a^6, d = ?? d = c *c ; // b = a^3, c = a^6, d = a^12 1 return b*d ; } L'état de l'environnement dans lequel s'eectue le calcul est décrit par les valeurs des trois variables locales. C'est donc ici un triplet d'entiers. Conformément à ce qui a été dit ci-dessus, chacune des trois aectations successives peut être vue comme une fonction transformant l'état en un autre. La première aectation change la valeur de la variable b et laisse les deux autres variables inchangées. let f1 = fun (b,c,d) -> (a*a*a,c,d) La deuxième aectation change la valeur de la variable c. let f2 = fun (b,c,d) -> (b,b*b,d) Et la troisième aectation change la valeur de d. let f3 = fun (b,c,d) -> (b,c,c*c) L'état initial des trois variables n'est pas déterminé par leur déclaration. Leur valeur peut donc être quelconque. On la pose ici arbitrairement égale à 0. La séquence de calcul peut donc être vue comme l'enchaînement suivant : f1 f2 f3 (0, 0, 0) −→ (a3 , 0, 0) −→ (a3 , a6 , 0) −→ (a3 , a6 , a12 ). Une traduction de la fonction C ci-dessus est obtenue en traduisant la séquence des trois aectations par une composition des trois fonctions. let puissance15 = fun a -> let f1 = fun (b,c,d) -> (a * a * a, c, d) and f2 = fun (b,c,d) -> (b, b * b, d) and f3 = fun (b,c,d) -> (b, c, c * c) in let b,c,d = f3 (f2 (f1 (0, 0, 0))) in b * d La traduction en style fonctionnel de la fonction puissance15 donnée ci-dessus suit un schéma très général de représentation des variables utilisées par la version impérative par un triplet d'entiers, les transformations successives étant elles représentées par composition de trois fonctions. Mais on pourrait en donner une autre version purement fonctionnelle et moins lourde comme par exemple let puissance15 let b = a * a let c = b * b let d = c * c b * d = fun a -> *a in in in 2.3 Conditionnelle 2.4 Itération non conditionnelle int expo(int a, int n) { int i,p ; p = 1 ; for (i=1;i<=n;i++) { p = p *a ; } return p ; } 2 (** itere : (’a -> ’a) -> int -> ’a -> ’a [itere f n e] = f (f ...(f e) ...) {b CU :} [n >= 0] *) let rec itere = fun f n e -> if n = 0 then e else itere f (n - 1) (f e) let expo = fun a n -> let _,_,p = itere (fun (a,i,p) -> (a,i+1,a*p)) n (a,1,1) in p 2.5 Itération conditionnelle int expo_rapide(int a, int n) { int r,s,k ; r = 1 ; s = a ; k = n ; while (k != 0) { if (k%2 == 1) r = r*s ; s = s *s ; k = k / 2 ; } return r ; } (** tantque : (’a -> ’a) -> (’a -> bool) -> ’a -> ’a *) let rec tantque = fun f p e -> if p e then tantque f p (f e) else e let expo_rapide = fun a n -> let r,_,_ = tantque (fun (r,s,k) -> if k mod 2 <> 0 then (r*s, s*s, k/2) else (r, s*s, k/2)) (fun (r,s,k) -> k <> 0) (1,a,n) in r 3