EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction EPITA Spé: Programmation Introduction à OCaml Marwan Burelle [email protected] http://wiki-prog.kh405.free.fr Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Plan EPITA Spé: Programmation Introduction à OCaml Marwan Burelle 1 Introduction Introduction Architecture des programmes OCaml 2 Architecture des programmes OCaml 3 Programmation Fonctionnelle 4 Programmation Impérative en OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Introduction EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Introduction Programmation Fonctionnelle Programmation Impérative en OCaml Objective Caml EPITA Spé: Programmation Introduction à OCaml Langage fonctionnel de la famille ML Langage fortement typée, doté d’un typage statique Le système de type propose un mécannisme d’inférence (type reconstruction) Le système de type est polymorphe Le langage dispose de l’ordre supérieur Le langage propose des extensions impératives Le langage propose un sous-langage de modules très évolué (modules imbriqués, foncteurs . . . ) Le langage propose un couche objet Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Programmation Fonctionnelle EPITA Spé: Programmation Introduction à OCaml Pas de notion variables Itérations au travers de la récursion Marwan Burelle Introduction Les fonctions sont le point centrale du langage Architecture des programmes OCaml Généricité au travers des fonctions de première classe et du polymorphisme Programmation Fonctionnelle Pas d’instruction : tout est expression et renvoie une valeur Pas d’effet de bord Les opérations sur une structure de donnée travaille toujours en renvoyant une copie. Programmation Impérative en OCaml Architecture des programmes OCaml EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Architecture Générale En règle général, un programme OCaml est composé d’une série de définition et de bloc de code à exécuter. Tout est exécuté : il n’y a pas de véritable différence entre une définition et un bloc de code exécuté. Example: (* Exemple de programme OCaml *) (* Definition de fonction *) let f x = x (* Blocs de code *) let y = begin print_string " Definition de la constante y\n"; 10 end let toto = ( print_int y; print_int f y; print_newline ()) EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Point d’entrée EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Il n’y a pas de notion de point d’entrée puisque tout le code est exécuté. Attention, exécuter ne signifie pas que le résultat sera visible ! Les définitions ne sont pas différentiées des blocs de code simples, le corps de la définition est exécuté et sa valeur résultat est associée au symbole définie. On utilise généralement le motif universel " " pour indiquer que le résultat ne sera pas réutilisé (pas de symbole créé.) Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Organisation Concrète En règle général, un programme OCaml se compose comme suit : Une série de définition de constantes ; Une série de définition de fonctions ; Une fonction particulière qui servira de point d’entrée ; Un bloc de code appelant la fonction principale. Example: (* Constantes *) let pi = acos ( -1.) (* Fonctions *) let surface_cercle r = 2. *. pi *. r *. r (* Point d’ entree *) let main () = let r = int_of_string ( Sys .argv .(1)) in print_float ( surface_cercle r) (* Appel du point d’ entree *) let _ = main () EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Compilation v.s. Interprétation L’interpréteur affiche un résultat pour toutes les phrases entrées : # 1+1 ;; - : int = 2 Les phrases composées uniquement d’une expression, bien qu’elles soient exécutées, n’ont pas d’effet visisble dans un programme compilé : # echo " 1+1;; " > toto.ml # ocamlc -o toto toto.ml # ./ toto # On évitera l’utilisation des ”;;” Un bloc de code qui ne renvoie rien (affichage, bloc impératif . . . ) sera introduit par une construction : let _ = ... EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Un programme complet Example: EPITA Spé: Programmation Introduction à OCaml Marwan Burelle (* Exemple de programme en OCaml *) Introduction (* Les constantes *) let pi = acos ( -1.) (* Les fonctions *) let surface r = 2. *. pi *. r *. r (* Fonction principale *) let main () = begin print_string "Rayon du cercle ?\n"; let r = read_float () in print_string " Surface du cercle de rayon "; print_float r; print_string " = "; print_float ( surface r); print_newline (); end (* Appel *) let _ = main () Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Programmation Fonctionnelle EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Fonctionnelle Programmation Impérative en OCaml Typage et Inférence EPITA Spé: Programmation Introduction à OCaml Le typage doit être respecté OCaml est capable de déduire tout seul le type des fonctions. Marwan Burelle Introduction Architecture des programmes OCaml Example: Programmation Fonctionnelle # 1 + true ;; This expression has type bool but is here used with type int # let f x = x + 1;; val f : int -> int = <fun > # let f x = x;; val f : ’a -> ’a = <fun > # let f g x = (x+1,g x);; val f : (int -> ’a) -> int -> int * ’a = <fun > # f string_of_int 3;; - : int * string = (4, "3") Programmation Impérative en OCaml Récursivité Example: Fichier fact.ml : let rec fact = function 0 | 1 -> 1 | x when x >0 -> x * fact (x -1) | _ -> assert false let main () = begin let x = int_of_string ( Sys .argv .(1)) in print_int (fact x); print_newline (); end let _ = main () Compilation et exécution : $ ocamlc -o fact fact.ml $ ./ fact 6 720 $ ./ fact 10 3628800 $ ./ fact 12 479001600 $ EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Listes ’a list Constructeurs : [] liste vide :: ajout en tête Opération : @ concaténation Example: # # # # # [];; : ’a list = [] 1::[];; : int list = [1] [1;2];; : int list = [1; 2] [1;2] @ [3;4];; : int list = [1; 2; 3; 4] let rec length = function [] -> 0 | _::t -> 1 + length t;; val length : ’a list -> int = <fun > # length [1;2;3];; - : int = 3 # EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Ordre Supérieur Example: # let rec map f = function [] -> [] | h::t -> (f h):: map f t;; val map : (’a -> ’b) -> ’a list -> ’b list = <fun > # let rec fold f a = function [] -> a | h::t -> fold f (f a h) t;; val fold : (’a -> ’b -> ’a) -> ’a -> ’b list -> ’a = <fun > # map float_of_int [1;2;3];; - : float list = [1.; 2.; 3.] # fold (+) 0 [1;2;3];; - : int = 6 # fold (fun a x -> x::a) [] [1;2;3;4;5;6;7];; - : int list = [7; 6; 5; 4; 3; 2; 1] # Fichier print list.ml : let rec iter f = function [] -> () | h::t -> f h; iter f t let maliste = [1;2;3;4;5;6;7] let main () = iter (fun x -> P r i n t f . printf " %i;" x) maliste ; print_newline () let _ = main () $ ocamlc -o print_list print_list .ml $ ./ print_list 1; 2; 3; 4; 5; 6; 7; EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Types Sommes Example: type ’a tree = Leaf of ’a | Node of ’a tree * ’a * ’a tree EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction let rec find x = function Leaf c | Node(_,c,_) when x = c -> true | Node(fg ,_,fd) -> (find x fg) || (find x fd) | _ -> false let rec list_of_tree = function Leaf c -> [c] | Node(fg ,c,fd) -> ( list_of_tree fg)@[c]@( list_of_tree fd) let arbre = Node(Node(Leaf 1,2, Leaf 3),4, Node(Leaf 5,6, Leaf 7)) let main () = begin L i s t .iter (fun x -> P r i n t f . printf "%i;" x) ( list_of_tree arbre ); print_newline (); print_string (if (find 5 arbre) then " trouve !\n" else "pas trouve !\n"); end let _ = main () Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Algortithmes et programmation fonctionnelle EPITA Spé: Programmation Introduction à OCaml Prennons le cas de listes triées d’entiers : elem = 0 elem = 3 elem = 6 Marwan Burelle Introduction Nous allons insérer de manière fonctionnelle la valeur 2 dans la liste : Architecture des programmes OCaml Programmation Fonctionnelle elem = 0 elem = 0 elem = 3 elem = 6 elem = 2 Si les choses sont faites correctement, la fin de la liste (qui n’est pas modifiée) est partagée entre l’ancienne liste (qui demeure inchangée) et la nouvelle. Pour plus de détail référez vous aux slides du premier cours. Programmation Impérative en OCaml Programmation Impérative en OCaml EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Programmation Impérative en OCaml Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Essayons de faire des boucles EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Example: Introduction (* Boucles qui ne marchent pas ... *) let _ = let x = 10 in while (x <0) do print_int x; let x = x - 1 in (* ? *) () done La construction let x = ... in ... définie un nouveaux symbole x à chaque fois. On voudrait modifier la valeur de x à chaque tour de boucle, ce qui est incohérent avec la programmation fonctionnelle. Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Données modifiables EPITA Spé: Programmation Introduction à OCaml Pour faire de la programmation impérative, il faut pouvoir modifier certaines données. Comment peut-on être fonctionnel et avoir des variables ? Idée : la donnée manipulée reste constante, mais on dispose d’un mécannisme d’accès pour la modifier. On parle de donnée muable (terme horrible en français, mais modifiable est un peu trop faible.) En OCaml, il existe plusieurs sortes de données muable : les références (pointeurs statiques), les champs d’enregistrements, les (cases des) tableaux, les chaı̂nes de caractères et les propriétés des objets. Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Les références Une référence est une sorte de pointeur, la valeur d’une référence est immuable, mais cette valeur est une adresse sur une valeur que l’on pourra modifier. Le polymorphisme sur les références est limité (les changements de valeur référencée doivent préserver le type d’origine.) Une référence pointe toujours sur une valeur (pas de problème de pointeur null.) Example: # let r = ref 0;; val r : int ref = { contents = 0} # !r;; - : int = 0 # (r := 2; !r);; - : int = 2 # r := "a" ;; This expression has type string but is here used with type int # EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Polymorphisme et références Example: EPITA Spé: Programmation Introduction à OCaml Marwan Burelle # let f x = x ;; val f : ’a -> ’a = <fun > # let r = ref f ;; val r : (’_a -> ’_a) ref = { contents = <fun >} # !r;; - : ’_a -> ’_a = <fun > (* ’_a is not a polymorphic type variable *) # (!r) 1;; - : int = 1 # !r;; - : int -> int = <fun > (* r is now a reference to a int -> int function *) # f true ;; - : bool = true # (!r) true ;; This expression has type bool but is here used with type int # r := fun x -> x + 1 ;; - : unit = () # !r 1;; - : int = 2 # Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Les tableaux Example: # let t = [| 1; 2; 3 |];; val t : int array = [|1; 2; 3|] # t .(0);; - : int = 1 # t.(0) <- 2;; - : unit = () # t .(0);; - : int = 2 # let f x = Array .make 10 x;; val f : ’a -> ’a array = <fun > # f true ;; - : bool array = [| true; true; true; true; true; true; true; true; true; true |] # let t2 = f (ref 0);; val t2 : int ref array = [|{ contents = 0}; { contents = 0}; { contents = 0}; { contents = 0}; { contents = 0}; { contents = 0}; { contents = 0}; { contents = 0}; { contents = 0}; { contents = 0}|] # t2 .(0) := 1 ;; - : unit = () # t2 ;; - : int ref array = [|{ contents = 1}; { contents = 1}; { contents = 1}; { contents = 1}; { contents = 1}; { contents = 1}; { contents = 1}; { contents = 1}; { contents = 1}; { contents = 1}|] # EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Les enregistrements Example: # type r = { a : int; mutable b : string } ;; type r = { a : int; mutable b : string ; } # let x = { a = 0; b = "" };; val x : r = {a = 0; b = ""} # x.b <- " bonjour " ;; - : unit = () # x;; - : r = {a = 0; b = " bonjour "} # let y = { x with a = 1 };; val y : r = {a = 1; b = " bonjour "} # x.b <- " bonjour2 " ;; - : unit = () # y;; - : r = {a = 1; b = " bonjour "} Example: # type r’ = { a : int ref; b : int };; type r’ = { a : int ref; b : int; } # let x = {a = ref 0; b = 1 };; val x : r’ = {a = { contents = 0}; b = 1} # let y = { x with b = 2 };; val y : r’ = {a = { contents = 0}; b = 2} # x.a := 1; y ;; - : r’ = {a = { contents = 1}; b = 2} EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Les chaı̂nes de caractères Example: # let s = " bonjour ";; val s : string = " bonjour " # s.[7] = ’\n ’;; - : bool = false # s.[7] <- ’\n ’;; - : unit = () # s;; - : string = " bonjour \n" Example: let maj s = for x = 0 to ( String . length s) - 1 do s.[x] <- Char. uppercase (s.[x]) done let main () = let s = String .copy Sys .argv .(1) in maj s; print_string s; print_newline () ; exit 0 let _ = main () EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml Le retour des boucles Example: let fibbo n = let i = ref 1 in let fib1 = ref 1 in let fib2 = ref 1 in while (!i < n) do fib1 := !fib1 + !fib2; fib2 := !fib1 - !fib2; i := !i + 1 done ; !fib1 let rec fib = function 0|1 -> 1 | n -> (fib (n -1)) + (fib (n -2)) let main () = let i = read_int () in P r i n t f . printf "par boucle : %i, par rec: %i\n" (fibbo i) (fib i); exit 0 let _ = main () EPITA Spé: Programmation Introduction à OCaml Marwan Burelle Introduction Architecture des programmes OCaml Programmation Fonctionnelle Programmation Impérative en OCaml