let - Linux
Transcription
let - Linux
OCaml caml.inria.fr « Pourquoi c'est (presque ;-) le meilleur langage du monde » Jeudi 7 avril 2005 – David Mentré OCaml en quelques mots ● ● OCaml est un langage de programmation : – fortement typé, à typage polymorphe – à inférence de type – types de données algébriques et filtrage – fonctionnel supérieur, impératif et objet – avec exceptions – avec un ramasse miettes – efficace – compilateur libre (QPL compatible DFSG & LGPL) Bref : que des bonnes raisons de l'utiliser ! ;-) Petit historique ● 1985 : langages de la famille ML (dont Caml) – développé à l'INRIA – comme support pour des assistants de preuve ● ● 1990 : système Caml Light – ● par ex. : Coq de l'INRIA byte code efficace 1996 : OCaml (Objective Caml) – objet – compilateur natif – ... et tout le reste Démarrage : les types ● Utiliser l'interpréteur pour découvrir OCaml ● Toutes les expressions sont typées $ ocaml Objective Caml version 3.08.2 # 1;; - : int = 1 # "toto";; - : string = "toto" # [|1; 2|];; - : int array = [|1; 2|] # [true; false];; - : bool list = [true; false] Types de base, fonctions ● Types classiques – booléens, entiers, flottants, chaînes, tuples, tableaux, listes, ... – structures de données plus complexes ● ensembles, table de hachage, ... Variables # let pi = 4.0 *. atan 1.0;; val pi : float = 3.14159265358979312 ●Fonctions # let carre x = x * x;; val carre : int -> int = <fun> ● Avantages du typage d'OCaml ● Avantage du typage fort ? – ● détection des erreurs dès la compilation Oui mais rentrer les types c'est lourd ! – c'est pourquoi on a une inférence de type ● le compilateur trouve les types, comme un grand # let carre_flottant x = x *. x;; val carre_flottant : float -> float = <fun> # let carre x = x * x;; val carre : int -> int = <fun> # carre 4.0;; This expression has type float but is here used with type int Type Somme et filtrage ● Définir les différentes alternatives # type chocolat = Noir | Lait;; type chocolat = Noir | Lait # let j_aime = Noir;; val j_aime : chocolat = Noir ● Traiter chaque cas dans une fonction # let bon_chocolat choco = match choco with | Noir -> true | Lait -> false;; val bon_chocolat : chocolat -> bool = <fun> # bon_chocolat Noir;; - : bool = true Avantage du filtrage ● On n'oublie pas un cas ! – si on l'utilise de manière systématique, ces bugs sont évités ● le compilateur le dit # let bon_chocolat choco = match choco with | Noir -> true;; Warning: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: Lait val bon_chocolat : chocolat -> bool = <fun> – structures de données correctes par construction Polymorphisme ● Une fonction est générique – elle marche sur plusieurs types ('a ≡ ) # List.rev;; - : 'a list -> 'a list = <fun> # List.rev [1; 2; 3; 4];; - : int list = [4; 3; 2; 1] # List.rev ["langage"; "super"; "un"];; - : string list = ["un"; "super"; "langage"] # List.rev [Noir; Lait];; - : chocolat list = [Lait; Noir] # List.map;; - : ('a -> 'b) -> 'a list -> 'b list = <fun> Avantage du polymorphisme ● Les programmes sont génériques – fonctionnalités similaires dans d'autres langages ● – ● ex. : Ada, Eiffel, template C++ en OCaml, le polymorphisme arrive tout seul avec l'inférence de type Combinable avec d'autres constructions – comme les types Somme – programmation claire et efficace Filtrage et type somme polymorphe # type 'a résultat = Trouvé of 'a | Pas_trouvé;; type 'a résultat = Trouvé of 'a | Pas_trouvé # let rec cherche elt liste = match liste with | tete :: reste when tete = elt -> Trouvé tete | tete :: reste when tete <> elt -> cherche elt reste | _ -> Pas_trouvé;; val cherche : 'a -> 'a list -> 'a résultat = <fun> # cherche Noir [Noir; Lait];; - : chocolat résultat = Trouvé Noir # cherche 0 [1; 2; 3; 4];; - : int résultat = Pas_trouvé Fonctions d'ordre supérieur Des fonctions qui prennent en argument d'autres fonctions ● – utile pour être générique # List.filter;; - : ('a -> bool) -> 'a list -> 'a list = <fun> # List.filter (fun x -> x = 2) [1; 2; 3; 4];; - : int list = [2] # List.filter (fun x -> x mod 2 = 0) [1; 2; 3; 4];; - : int list = [2; 4] # List.filter (fun x -> x >= 2 && x < 4) [1; 2; 3; 4];; - : int list = [2; 3] Style fonctionnel ● Utiliser des fonctions récursives – variables à assignation unique ● – on leur donne une seul valeur : let x = ... in .... factorielle : n! = 1 * 2 * ... * n-1 * n # let rec factorielle x = if x <= 1 then 1 else x * factorielle (x - 1);; val factorielle : int -> int = <fun> # factorielle 5;; - : int = 120 Style impératif ● Comme la programmation « classique » – variables peuvent être modifiées plusieurs fois # let factorielle n = let result = ref 1 in for i = 2 to n do result := i * !result done; !result;; val factorielle : int -> int = <fun> # factorielle 5;; - : int = 120 Remarques sur le style fonctionnel ● C'est efficace ? – Oui car : ● une fonction récursive terminale est équivalente à une boucle – ● ● et le compilateur OCaml fait la transformation (comme gcc) en sous-main, on a un jeu de pointeur, pas de copie réelle de données Avantages : fonctionnel ou impératif ? – cela dépend :-) – le style fonctionnel permet souvent une programmation plus propre et plus lisible Programme récursif terminal Fonctionnel # let rec factorielle x = ● if x <= 1 then 1 else x * factorielle (x - 1);; val factorielle : int -> int = <fun> # let factorielle x = ●Fonctionnel let rec f x acc = if x <= 1 then acc else f (x - 1) (x * acc) in terminal f x 1;; val factorielle : int -> int = <fun> Impératif ● # let factorielle n = let result = ref 1 in for i = 2 to n do result := i * !result done; !result;; val factorielle : int -> int = <fun> Exemple de programme fonctionnel # let rec tri liste = match liste with | [] -> [] | x :: l -> insère x (tri l) and insère elem liste = match liste with | [] -> [elem] | x :: l -> if elem < x then elem :: x :: l else x :: insère elem l;; val tri : 'a list -> 'a list = <fun> val insère : 'a -> 'a list -> 'a list = <fun> # tri [2; 1; 0];; - : int list = [0; 1; 2] Style objet ● ● Comme en C++, Java, ... – méthodes privées/publiques, virtuelles, ... – héritage multiple – liaison tardive – ... Avec en plus – objets immédiats (objet sans classe) – méthodes polymorphes – objets fonctionnels Exemple de programme objet # class point = object val mutable x = 0 method get_x = x method move d = x <- x + d end;; class point : object val mutable x : int method get_x : int method move : int -> unit end # let p = new point;; val p : point = <obj> # p#get_x;; - : int = 0 # p#move 3;; - : unit = () # p#get_x;; - : int = 3 Exceptions ● Comme en C++, Java, ... – mais efficace – donc utilisable comme structure de contrôle, pas seulement sur les cas d'erreur # exception Trouvé_à_l'index of int;; exception Trouvé_à_l'index of int # let cherche x tableau = for i = 0 to Array.length tableau - 1 do if tableau.(i) = x then raise (Trouvé_à_l'index i) done;; val cherche : 'a -> 'a array -> unit = <fun> # cherche 3 [|1; 4; 3; 7; 8|];; Exception: Trouvé_à_l'index 2. Ramasse miettes ● Récupération automatique de la mémoire – ● ramasse miettes, Garbage Collector (GC) On alloue des variables, fonctions, ... – ... la libération est automatique – pas de segfault ● ● toute une classe de bugs en moins ! Ramasse miettes efficace – contrairement à Java (du moins au début) – techniquement : GC générationnel incrémental ● générationnel : vieux objets et petits nouveaux ● incrémental : un petit coup de GC de temps en temps Compilation ● Deux compilateurs – – byte code sur une machine virtuelle (ocamlc) ● marche sur toutes les plate-formes ● efficace ● utilisé dans l'environnement interactif code natif (ocamlopt) ● ● ● pour x86, IA64, PowerPC, AMD64, Alpha, SPARC, MIPS, PA-RISC, StrongARM très efficace Plate-formes supportées – Unix (Linux, *BSD, propriétaires), Windows, MacOS X Performances ● OCaml est très efficace – « au pire, 2 fois plus lent que le C » – en pratique, quasiment aussi rapide que le C ● voire plus (gain sur l'algorithmique) shootout.alioth.debian.org CPU : x1 Mem : x1 Lignes : x0 1 2 3 4 ... 8 ... 13 ... 18 19 20 21 22 ... 24 langage C Intel C gcc OCaml Ada 95 GNAT ... C++ Intel ... C# Mono ... Python Perl Tcl Java Ruby ... PHP score 92,26 85,99 76,52 63,49 ... 44,67 ... 34,25 ... 29,59 27,92 26,47 22,07 18,15 ... 15,15 Calcul scientifique Sieve primes up to 10^8 (bit-twiddling/array limited): 32-bit OCaml: 7.102s 32-bit C++: 19.145s 64-bit OCaml: 5.697s 64-bit C++: 13.433s 100th-nearest neighbours from a 10k-atom model of amorphous silicon (de/allocation limited): 32-bit OCaml: 28.407s 32-bit C++: 14.035s 64-bit OCaml: 35.538s 64-bit C++: 12.392s Generate, bubble sort and accumulate an array of 10^4 double-precision random floatingpoint numbers: 32-bit OCaml: 1.185s 32-bit C++: 1.471s 64-bit OCaml: 0.785s 64-bit C++: 0.957s without bounds checking: 32-bit OCaml: 0.992s 32-bit C++: 1.249s 64-bit OCaml: 0.591s 64-bit C++: 0.705s Jon Harrop / 2005-03-30 / caml-list 2048^2 mandelbrot (float-arithmetic limited): 32-bit OCaml: 2.946s 32-bit C++: 1.479s 64-bit OCaml: 1.704s 64-bit C++: 1.161s 1024 FFTs and iFFTs (float-arithmetic limited): 32-bit OCaml: 31.491s 32-bit C++: 8.441s 64-bit OCaml: 9.260s 64-bit C++: 8.562s Accumulate a Lorentzian over the number of integer triples (i, j, k) which lie in i^2 + j^2 + k^2 < 400 (float-arithmetic limited): 32-bit OCaml: 16.329s 32-bit C++: 8.002s 64-bit OCaml: 9.459s 64-bit C++: 5.933s Utilisation d'OCaml ● A éviter en OCaml – programmation système et temps réel dur – boucles bien tassées ● – petits scripts, traitement du texte, bidouille ● – Perl, Python, shell script programmation objet « pure » (à mon avis) ● ● C, C++, Ada C++, SmallTalk, Eiffel, Python Pour tout le reste, OCaml est utilisable – soit 90 % de la programmation courante Environnement ● Environnement de programmation complet – debugger, compilateurs, lex et yacc, éditeurs (mode Emacs), threads, ... – pleins de bibliothèques, d'exemples, ... ● ● The Hump : http://caml.inria.fr/cgi-bin/hump.en.cgi Communauté à croissance exponentielle – liste de diffusion (très) active ● – voire trollesque mais quand même plus petite que C, C++, Java Ce dont je ne parlerai pas ● Modules fonctionnels (functors) – encapsulation et abstraction des données ● Liaison avec le langage C ● Labels # let f ~grand ~petit = grand - petit;; val f : grand:int -> petit:int -> int = <fun> # f ~grand:3 ~petit:2;; - : int = 1 # f ~petit:2 ~grand:3;; - : int = 1 ● Debugger avec retour arrière temporel ● Évaluation paresseuse Et les inconvénients !? ● Langage de recherche – plus difficile de trouver un programmeur – le langage évolue (mais en bien :-) ● Pas autant de bibliothèques que Java ou C ● Pas de debugger sur le code natif ● Messages d'erreur parfois cryptiques ● Quelques contraintes liées au typage – pas de récursivité entre modules, ordre des fichiers au link un exemple : demexp ● Programme OCaml complet – réseau, serveur, client graphique (GTK2), XML, threads – ~10.000 lignes de source – ~6.000 lignes de code OCaml ● ● ½ serveur, ½ client – versions Linux et Windows – 2 ans de développement Bibliothèques externes – ocamlrpc, CDuce Conclusion ● OCaml a de réelles qualités – efficace, sûr, agréable, bien outillé, portable – on évite des bugs par construction – c'est un vrai plaisir de programmer avec ● ● aussi rapide que le C, aussi sûr que l'Ada, aussi souple que le Lisp (voire plus que les trois :-) Utilisez le pour l'apprécier ! Pour démarrer ● ● ● Site officiel – http://caml.inria.fr/ – listes de discussions, sources, pointeurs Un livre – http://www.pps.jussieu.fr/Livres/ora/DA-OCAML/index.html – apprendre le langage La bosse du chameau – http://caml.inria.fr//cgi-bin/hump.fr.cgi – trouver la roue Questions ? ?