You are on page 1of 17

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

Notice d'utilisation 2005 2006 2007 Florent Monnier


Ce document est distribu sous licence FDL, ce qui signifie que vous pouvez le redistribuer avec ou sans amliorations du moment d'en conserver le droit d'auteur (copyright) et la licence. Vous tes galement invit m'crire pour me suggrer des amliorations, ou simplement pour me soumettre des suggestions

Merci Xavier Hienne pour la relecture.

Vous pouvez aussi trouver le contenu de cette page wikifie.

Introduction OCaml
Objective Caml (not OCaml par la suite) est un langage de script et de programmation, ce document pour but d'en proposer une introduction concise et d'tre accessible tous, et ce, mme sans connaissances pralable en programmation ou scripting. Si vous tes dbutant et que vous ne comprennez pas certaines parties, n'hsitez pas me le signaler, ou me demander des prcisions sur certains points, ou au contraire en apporter pour enrichir ce didacticiel

Tout d'abord une courte prsentation d'Objective Caml (OCaml). Il s'agit d'un projet initi par l'INRIA. Le langage OCaml fournit une exceptionnelle suret d'excution notament grce au fait que tout ses lments soient TRS FORTEMENT TYPS . Ce dernier point require un certain temps d'adaptation au dbut, mais est une aide trs prcieuse par la suite.

L'interprteur intractif Les types primitifs Le Transtypage Les tableaux Les listes Les Tuples Les Fonctions Condition IF La Boucle FOR Les Boucles avec des Fonctions Filtrage avec MATCH Iteration sur les listes et les tableaux Les Enregistrements Les Variants Mise en garde contre les tableaux Rcursivit Terminale Script et excutable

1 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

Les diffrents modes d'excution OCaml fournit plusieurs modes d'excution possibles :

en BINAIRE, code natif compil


(comme le C et avec des performances quivalentes)

en BYTECODE excut l'aide d'une MACHINE VIRTUELLE


(comme Java et avec des performances quivalentes)

en INTERPRT avec l'interprteur


(comme Perl ou PHP et avec des performances quivalentes)

en INTERACTIF avec une boucle d'interaction nomme "oplevel" t


(comme avec la commande 'python')

Tous ces modes d'excution seront vus plus en dtail ultrieurement. Dans ce premier pisode nous n'utiliserons que la boucle d'interaction. Les styles de programmation OCaml offre deplus la possibilit d'adopter la plupart des styles de programmation : fonctionnel, impratif, objet, modulaire et de les mlanger. Malgr tout OCaml se prte particulirement bien au style de programmation fonctionnel, car utilis correctement, les effets de bord tant alors vits, et associ au typage fort, la suret d'excution est alors excellente, et il est virtuellement possible de raliser des logiciels garantis sans aucuns bugs. Voici pour vous mettre l'eau la bouche et vous donner l'envie de continuer. Maintenant pour pouvoir suivre le premier volet de cette initiation, tlchargez et installez OCaml partir de : http://caml.inria.fr/ocaml/release.fr.html ou bien l'aide de l'installeur fournit par votre distribution.

Lancez l'interprteur intractif


Maintenant la premire chose faire est de commencer se familiariser avec le systme de typage d'OCaml. Rassurez-vous, mme si vous trouverez peut-tre cela contraignant au dbut, on intgre trs vite ce systme pour l'utiliser de manire naturelle. Pour commencer s'y frotter, le meilleur moyen est d'utiliser l'interprteur interactif (le "toplevel"). Pour cela commencez une nouvelle session en tapant dans votre console la commande 'ocaml'. La version d'OCaml sera alors affiche en guise d'introduction, suivie du prompt d'OCaml qui attendra que vous entriez la premire expression.
[blue_prawn@azur ~]$ ocaml Objective Caml version 3.09.3 #

Toutes les expressions ou suite d'expressions doivent se terminer par deux points virgule comme ceci ';;'. Pour comprendre le systme de typage nous allons tout d'abord voir les types primitifs qui sont les lments de bases.

2 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

Les types primitifs 1) Le type entier (dcimal), tapez au prompt :


# 4 ;;

OCaml vous indiquera alors en rponse le type de l'expression que vous venez d'entrer, suivit de sa valeur :
- : int = 4

2) Le type nombre virgule (flottante) :


# 8.6 ;; - : float = 8.6

Le point est obligatoire pour indiquer le type flottant, mme s'il n'y a rien aprs :
# 12. ;; - : float = 12.

Sinon nous aurions un entier :


# 12 ;; - : int = 12

3) Le type boolen, dont la valeur vaut soit vraie soit fausse :


# # true ;; : bool = true false ;; : bool = false

4) Le type caractre (un et un seul caractre). Ils sont indiqus entre guillemets simple :
# 'e' ;; - : char = 'e'

5) Le type chane de caractres (zro ou plusieurs caractres), places entre guillemets doubles :
# "Objective Caml" ;; - : string = "Objective Caml"

L'oprateur de concatnation des chanes de caractre est :


# "Objective " ^ "Caml";; - : string = "Objective Caml"

Pour accder un des caractres d'une chane de caractres, vous pouvez procder comme suis :
# "Objective Caml".[3] ;; - : char = 'e'

Comme vous pouvez le constater le type renvoy est bien le type caractre char vu prcdemment. L'index commence zro pour le premier caractre. Si l'on tente d'accder un caractre au del du dernier, une exception est leve :
# "Objective Caml".[26] ;; Exception: Invalid_argument "index out of bounds".

Dfinir, lever et rattraper des exceptions sera vu ultrieurement. 6) Le type unit :


# () ;; - : unit = ()

Ce type est une sorte d'entit vide. Il est utilis par exemple lorsqu'une fonction ne prend

3 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

pas d'argument particulier. C'est galement le type renvoy par une fonction qui ralise une action mais ne renvoie aucune valeur particulire. Ce type n'est pas proprement parl l'quivalent de ce que l'on retrouve sous le nom de void ou NULL dans d'autres langages, car l'usage du type unit peut aller bien au del. Une fonction n'ayant pas une relle valeur de retour retourne le type unit :
# print_endline "OCaml" ;; OCaml - : unit = ()

Ici la fonction ralise une action (afficher du texte) et ne retourne rien, part le type unit .

Transtypage Comme il est dit et rpt OCaml est fortement typ pour viter les erreurs de programmation. Il n'est ainsi pas possible de faire le transtypage (convertir le type) implicitement d'un lment, il faut le faire explicitement !
(Vous rencontrerez parfois aussi le terme "coercition" la place de transtypage.)

Pour obtenir un nombre virgule partir d'un entier :


# float_of_int 16 ;; - : float = 16.

Et inversement :
# int_of_float 12.0 ;; - : int = 12

Pour obtenir le code ascii (encodage de caractres) d'un caractre :


# int_of_char 'A' ;; - : int = 65

Et inversement :
# char_of_int 97 ;; - : char = 'a'

Idem pour raliser des oprations arithmtiques, les nombres doivent tre de mme type :
# 4 + 6 ;; - : int = 10

Les oprateurs arithmtiques agissant sur les flottants doivent tre suffixs par un point :
# 2.4 *. 1.2 ;; - : float = 2.88

Combinaison des deux types avec transtypage :


# (float) 9 /. 1.2 ;; - : float = 7.5

Pour raliser un transtypage il ne s'agit pas d'indiquer le type souhait avant l'expression entre parenthse comme pourrait le suggrer cet exemple, il faut utiliser une fonction qui ralise la conversion de type. En l'occurrence ici float n'est qu'un alias de la fonction float_of_int vue plus haut, et placer cette fonction dans un tuple (les tuples seront vus plus bas) n'influe en rien sur son fonctionnement.

Pour dfinir un lment nomm (une valeur ou une fonction) on utilise le constructeur
4 sur 17 06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

'let' : # let my_item = 128 ;; val my_item : int = 128

Ci-dessus l'lment est de type entier int, il pourra donc tre utilis partout o un entier int est attendu. OCaml, en rponse l'entre, indique ici en plus le nom de l'lment. (Ce nom ne peut pas commencer par une majuscule ou un chiffre.) Vrifiez que le lien a bien t dfini :
# my_item ;; - : int = 128

L'expression dfinie ci-dessus n'est pas proprement parler ce que l'on appelle une variable dans d'autres langages (on pourrait les voir comme des variables constantes), car sa valeur ne peut tre "rellement modifie", mme s'il est possible d'en crer une nouvelle portant le mme nom (et ce mme partir de la prcdante) :
# let my_item = my_item * 2 ;; val my_item : int = 256

Mais il vaux mieux viter ce genre de chose car c'est le dbut d'une drive vers le style impratif. Si lors d'une modification ultrieure du programme vous supprimez la "redfinition", ce sera donc la prcdante qui sera utilise, et cela peut potentiellement tre inappropri. Dans le cas ci-dessus il vaut bien mieux utiliser un nouveau nom plutt que de crer un homonyme (qui aura d'ailleur peut-tre un type diffrent). Prendre un nom diffrent ne sera pas plus gourmand (ni en temps, ni en espace). Les variables modifiables transversalement sera vu ultrieurement, cependant leur usage est trs fortement dconseill car le fait que leur valeur soit accessible transversalement induit des effets de bords, souvent l'origine de bugs. Il est pratiquement toujours possible d'utiliser une valeur nomme plutt qu'une variable.

OCaml fournit plusieurs structures permettant de regrouper des lments comme les types primitifs vu en dbut du didacticiel : les tableaux (type 'array'), les listes (type 'list') et les tuples.

LES TABLEAUX
Les tableaux sont de dimension fixe, et ne doivent contenir que des lments du mme type :
# [| 'b'; 'E'; 't'; 'i' |] ;; - : char array = [|'b'; 'E'; 't'; 'i'|] # let my_array = [| 1; 5; 7; 12; 64 |] ;; val my_array : int array = [|1; 5; 7; 12; 64|]

Les tableaux sont adapts pour pouvoir accder un lment (n'importe lequel) partir d'un index donn :
# my_array.(2) ;; - : int = 7

L'accs n'importe quel lment d'un tableau se fait en vitesse constante, ce qui n'est pas le cas avec les listes, pour lesquelles il faudrait dpiler tous les lments avant d'accder l'lment que l'on souhaite retrouver.

5 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

LES LISTES
Les listes ne doivent galement contenir que des lments du mme type.
# [ 1; 3; 45; 24; 17; 0; 9 ] ;; - : int list = [1; 3; 45; 24; 17; 0; 9]

On ralise l'empilement et le dpilement des lments uniquement au sommet de la pile. Les listes ne sont donc pas de taille fixe comme les tableaux.
# let my_list = [ 3; 4; 5 ] ;; val my_list : int list = [3; 4; 5] # 8 :: my_list ;; - : int list = [8; 3; 4; 5]

Elle sont bien adaptes pour raliser des piles d'lments, dans lesquelles on va venir empiler et dpiler des lments au sommet de la pile. (Pensez pour cela une pile d'assiette, au sommet de laquelle on vient ajouter les assiettes laves, puis au sommet de laquelle on va venir prendre des assiettes propres lorsqu'on en a besoin.) L'empilement et le dpilement d'lments peuvent tre raliss respectivement partir et jusqu' la liste vide :
# [] ;; - : 'a list = []

(Le 'a indique alors que le type est indfini.) Les listes sont bien adaptes lorsque ses lments doivent tre parcourus de manire squentielle.

LES TUPLES
Les tuples permettent de regrouper des lments qui peuvent tre de type diffrent. Ils regroupent gnralement un petit nombre d'lments. Il sont utiliss par exemple lorsqu'une fonction doit retourner plusieurs lments (qui seront alors regroups dans un tuple).
# (3, 'F') ;; - : int * char = (3, 'F') # let val a # let val b a : b : = 3 ;; int = 3 = 'F' ;; char = 'F'

# let (a, b) = (b, a) ;; val a : char = 'F' val b : int = 3 # # a : b : ;; char = 'F' ;; int = 3

Un tuple peut contenir n'importe quoi, un nombre, une chane de caractre, un tableau ou mme une fonction :
# (12.5, "ICCF", float_of_int) ;; - : float * string * (int -> float) = (12.5, "ICCF", <fun>)

Et comme nous l'avons dit, en OCaml tout a un type, mme les fonctions ont leur type. Nous

6 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

allons voir a plus bas.

Les Fonctions
L'environnement OCaml fournit par dfaut un ensemble de fonctions contenues dans sa librairie standard, compose de plusieurs "modules". Le nom des modules commence toujours par une majuscule, par exemple le module String qui fournit des fonctions pour manipuler les chanes de caractres. Une des fonctions fournies par le module String est la fonction 'length'. Pour pouvoir l'utiliser il faut soit faire prcder le nom de la fonction par le nom du module, soit ouvrir le module 'String' au pralable. Voici les deux mthodes :
# String.length "Objective Caml" ;; - : int = 14 # open String ;; # length "Objective Caml" ;; - : int = 14

Une fois ouvert, les fonctions d'un module sont accessibles dans toute la suite de la session ou du fichier de code source. OCaml est un langage expression. Chaque lment est une expression, et chaque expression a un type. Ceci s'exprimente ainsi en tapant juste le nom d'une fonction, le type de la fonction est alors indiqu :
# length ;; - : string -> int = <fun>

Le type d'une fonction est en quelque sorte ce que l'on appelle le prototype d'une fonction dans d'autres langages, mais ce n'est pas totalement quivalent en raison des possibilits d'application partielle d'OCaml qui seront vues ultrieurement. Ici on voit que la fonction 'length' prend un argument de type 'string' (chaine de caractre) et qu'elle renvoie un lment de type 'int' (entier). OCaml tant fortement typ, il est ainsi utile de vrifier le type d'une fonction avant de l'utiliser, afin de bien avoir en tte le type des paramtres lui passer, et le type du ou des lments ou de l'expression de retour de la fonction. On peut ainsi vrifier le type des fonctions utilises au tout dbut de cette introduction OCaml :
# # # float_of_int ;; : int -> float = <fun> int_of_char ;; : char -> int = <fun> print_string ;; : string -> unit = <fun>

Ces fonctions font partie du module 'Pervasives', qui est le module par dfaut (il n'y a pas besoin de l'ouvrir pour en utiliser ses fonctions). Nous avons galement voqu le module String juste avant, chaque module a une page de documentation dans le manuel d'OCaml. Cependant pour utiliser certaines de ces fonctions, vous aurez probablement besoin d'en connatre un petit peu plus sur OCaml avant, donc lisez la suite.

Pour dfinir une fonction nomme on utilise le constructeur 'let', comme pour dfinir une

7 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

valeur :
# let step a = a + 2 ;; val step : int -> int = <fun> # step 6 ;; - : int = 8

OCaml tant un langage expression il n'est point besoin d'utiliser une instruction 'return' comme dans d'autres langages, on donne directement l'expression qui doit tre renvoye.

Dans un programme ou un script, nous avons souvent besoin de pouvoir excuter certaines parties de codes plutt que d'autres. OCaml offre plusieurs structures de contrle pour cela. (Si vous connaissez dj des langages de programmation ou de script, vous serez peut-tre un peu dpays par celles d'Objective Caml.)

Condition IF
La faon la plus simple d'utiliser la structure 'if', est de lui fournir une comparaison de ce genre :
# 2 > 3 ;; - : bool = false

ici 2 n'est pas suprieur 3 donc le boolen 'false' est retourn. (Pour l'galit utilisez de prfrence = == sauf si vous tes sr que vous recherchez strictement l'galit physique de la zone de RAM point.) La structure de contrle 'if' doit obligatoirement utiliser le type boolen (bool) :
# if 2 > 3 then "first case" else "second case" ;; - : string = "second case"

On peut dvelopper l'expression prcdente :


# let condition = (2 > 3) ;; val condition : bool = false # if condition then "la condition est vraie" else "la condition est fausse" ;; - : string = "la condition est fausse"

Comme les fonctions qui n'utilisent pas d'instruction return, cette structure de contrle renvoie directement une expression, qui peut tre utilise par exemple pour dfinir une valeur nomme :
# let lang = "OCaml" ;; val lang : string = "OCaml" # let my_item = if lang.[0] = 'O' then "Objective" else "Unknown" ;; val my_item : string = "Objective"

Enchainement de structures if (forts d'else if) :


8 sur 17 06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

# let comp a = if a = 4 then "equal" else if a > 4 then "sup" else "inf" ;; val comp : int -> string = <fun> # comp 6 ;; - : string = "sup"

Afin de garantir le type retourn par la structure if, ses deux branches then et else doivent imprativement retourner des lments de mme type (ce qui est donc le type de l'ensemble de l'expression). Le seul cas o il n'y a pas besoin de spcifier la seconde branche else c'est lorsque la premire branche est de type unit .
# let is_equal_to_5 this = if this = 5 then print_endline "this is equal to 5" ;; val is_equal_to_5 : int -> unit = <fun> # is_equal_to_5 - : unit = () 3 ;;

# is_equal_to_5 5 ;; this is equal to 5 - : unit = ()

Les Boucles
Boucle FOR
# for var = 2 to 6 do print_endline ("var value is " ^ (string_of_int var)) done ;; var value is 2 var value is 3 var value is 4 var value is 5 var value is 6 - : unit = ()

Ici nous voyons apparatre comme type de retours pour l'ensemble de l'expression le type unit . En effet la structure de contrle for correspond au type de programmation imprative, et elle ne peut contenir que des lments dont la valeur de retours est le type unit . Pour placer plusieurs instructions l'intrieur d'une boucle for, utilisez le sparateur point virgule ';' entre chaque instruction comme ceci :
# for var = 2 to 6 do print_string "var value is " ; print_int var ; print_newline () done

9 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

;; var value is 2 var value is 3 var value is 4 var value is 5 var value is 6 - : unit = ()

Pour viter le style impratif de cette structure de contrle, OCaml fournit une alternative offrant beaucoup plus de possibilits, et surtout une suret trs largement accrue.

Les Boucles avec des Fonctions


Il est possible de raliser des boucles grce des fonctions qui s'appellent elles-mmes, ce qui ralise la boucle. Il faut ajouter le mot-clef 'rec' (pour rcursif) lors de la dfinition de ces fonctions.
# let rec loop a = print_int a; print_newline(); if a > 0 then loop (a - 1) ;; val loop : int -> unit = <fun> # loop 5 ;; 5 4 3 2 1 0 - : unit = ()

Le grand danger cependant avec des boucles ralises de cette manire est d'en crire une qui ne s'arrtent jamais. Il faut donc prter un soin tout particulier la condition d'arrt. Dans l'exemple ci-dessus, la fonction s'appelle elle-mme avec un argument sans cesse infrieur, il nous est donc possible de raliser le rebouclage que lorsque l'argument est suprieur une certaine valeur, puisqu'il descendra forcment un moment en dessous de celle-ci.

MATCH
Dans son utilisation basique la plus rudimentaire, cette structure de contrle pourrait tre vue comme un quivalent de la structure de contrle switch que l'on connait dans d'autres langages de programmation et de script. ( ceci prt que match permet d'aller bien au del.) Voici un exemple simple :
# let var = 2 ;; val var : int = 2 # match var with | 1 -> "un" | 2 -> "deux" | 3 -> "trois" | _ -> "je ne sais pas compter au del de trois"

10 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

;; - : string = "deux"

Ici la valeur de var est 2, l'lment correspondant aprs la flche est donc retourn. L'englobement du match doit toujours tre exhaustif, d'une part, et toutes les expressions suivant chaque englobement doivent tre de mme type. Ce type sera ainsi le type de l'ensemble de l'expression match . (encore cette fameuse histoire de typage fort destin assurer le bon fonctionnement de votre code). Le dernier englobement _ est utilis si aucune des entres le prcdant ne correspond.
(L'englobement _ est peu prs quivalent de la directive default des structures 'switch' d'autres langages.)

L'englobement peut parfois tre exhaustif sans celui-ci (voir l'exemple la fin). Prcdemment nous avions vu comment empiler un lment une liste. Voici comment en dpiler un :
# let li = [ 2; 8; 5; 11; 32 ] ;; val li : int list = [2; 8; 5; 11; 32] # match li with | head :: tail -> tail | [] -> [] ;; - : int list = [8; 5; 11; 32] (* Dans le jargon, on dsigne le premier lment d'une liste comme sa tte (head en anglais), et la suite comme sa queue (tail). Ici l'expression renvoie la queue de la liste nomme li . *) # match li with | head :: tail -> print_string "the head is "; print_int head; print_newline(); | [] -> print_endline "the list is empty"; ;; the head is 2 - : unit = () (ici on voit aussi apparatre du commentaire entre les squences '(*' et '*)')

En utilisant cette possibilit de dsempilement au sein d'une boucle faite avec une fonction rcursive, cela permet alors de parcourir tous les lments d'une liste.
# let li = [2; 3; 5; 7] in let rec loop li = match li with | head :: tail -> print_int head; print_newline(); loop tail; | [] -> print_endline "The End"; in loop li ;; 2 3 5 7 The End - : unit = ()

11 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

"FOREACH"
(Iteration sur les listes et les tableaux) Si vous connaissez d'autres langages et que vous recherchez un quivalent de la structure foreach, la mme chose peut-tre ralise pour parcourir tous les lments d'une liste avec les fonctions iter (impratif) et map (fonctionnel) du module 'List' de la librairie standard d'OCaml.
# let for_each item = item * 2 ;; val for_each : int -> int = <fun> # let my_list = [ 5; 1; 22; 3; 16 ] ;; val my_list : int list = [5; 1; 22; 3; 16] # List.map for_each my_list ;; - : int list = [10; 2; 44; 6; 32]

Ici la fonction for_each est applique successivement chaque lment de la liste my_list. Ainsi la fonction prend en argument un des lments de la liste (qui doit donc avoir le mme type que l'arguement de la fonction), et les rsultats successifs de la fonction sont empils dans une nouvelle liste qui est le rsultat global de l'appel List.map for_each my_list. Cette opration fonctionne donc comme un filtre. Si la fonction for_each n'est utilise qu' un seul endroit du code, elle peut tre dfinie localement au sein de l'expression en cours, plutt que globalement. (On pourrait tenter la comparaison avec les fonctions inline de C++) Il faut alors la terminer par 'in' la place de ';;' comme ceci :
# let each item = item + 4 in List.map each [10; 50; 30; 80; 20] ;; - : int list = [14; 54; 34; 84; 24]

Elle ne sera alors disponible et accessible que dans l'expression en cours, comme vous pouvez le vrifier :
# each ;; Unbound value each

Une fonction anonyme peut aussi tre utilise :


# List.iter (fun i -> print_int i ; print_newline()) [ 8; 64; 2; 16 ] ;; 8 64 2 16 - : unit = ()

# let high_level_languages = [ "LISP"; "Perl"; "Python"; "PHP"; "Ruby";

12 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

"OCaml" ] in List.iter (fun lang -> print_endline lang) high_level_languages ;; LISP Perl Python PHP Ruby OCaml - : unit = ()

Pour parcourir les lments d'un tableau au lieu d'une liste, il existe deux fonctions semblables iter et map dans le module 'Array'.

Les Enregistrements
Ce sont des types qui doivent tre dclars au pralable, c'est dire dfinis par l'utilisateur ou dans des modules. Ce type ressemble aux tuples sauf qu'il permet de nommer les diffrents champs ce qui en amliore la lisibilit, et le nom des champs permet d'accder plus facilement qu'avec un tuple chaque lments de ce type :
# type ville = { nom: string; superficie: float; population: int } ;; type ville = { nom : string; superficie : float; population : int; } # let paris = { nom = "Paris"; superficie = 105.40; (* km2 *) population = 2_101_816; (* habitants *) } ;; val paris : ville = {nom = "Paris"; superficie = 105.4; population = 2101816} # paris.population ;; - : int = 2101816

Les Variants
Les variants sont galement des types qui doivent tre dclars avec une dfinition. Voici un exemple librement inspir d'un exemple de David MENTRE :
# type chocolat = Chocolat_noir | Chocolat_blanc | Chocolat_au_lait ;; type chocolat = Chocolat_noir | Chocolat_blanc | Chocolat_au_lait # let quoi_en_faire tablette = match tablette with | Chocolat_noir -> "Je le mange goulment !" | Chocolat_au_lait -> "Je le refile mon frre." | Chocolat_blanc -> "Je le refile au chien... (discrtement)" ;; val quoi_en_faire : chocolat -> string = <fun> # quoi_en_faire Chocolat_noir ;; - : string = "Je le mange goulment !"

13 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

Mise en garde contre les tableaux


Attention les tableaux contrairement aux listes sont des structures de type imprative dont les lments sont modifis en place, et donc avec ce que l'on appel des effets de bord. Si une fonction en modifie un lment, cette modification prendra effet transversalement, comme le montre cet exemple :
# let arr = [| 1; 2; 3; 4; 5 |] ;; val arr : int array = [|1; 2; 3; 4; 5|] # let modif i v = arr.(i) <- v; ;; val modif : int -> int -> unit = <fun> # modif 2 1024 ;; - : unit = () # arr ;; - : int array = [|1; 2; 1024; 4; 5|]

Ainsi si une fonction modifie un tableau avant de s'en servir pour quelqu'usage que ce soit, celui-ci ne sera pas intact aprs l'appel cette fonction. Il faut y penser et y tre attentif pour en tenir compte. Ce type de problme n'existe pas avec les listes qui sont des structures de type fonctionnel. Le mme type d'effet de bord peuvent avoir lieu avec les chanes de caractres :
# let str = "OCaml" ;; val str : string = "OCaml" # let modif str i c = str.[i] <- c; ;; val modif : string -> int -> char -> unit = <fun> # modif str 1 'k' ;; - : unit = () # str ;; - : string = "Okaml"

Ainsi si une fonction a besoin d'utiliser une version modifie d'un tableau ou d'une chane de caractre et que le programme en cours aura encore besoin de leur version originale aprs l'appel de fonction en question, vous pouvez utiliser une copie locale la place de l'originale avec les fonctions String.copy ou Array.copy.

Rcursivit Terminale
La rcursivit terminale est une notion trs importante matriser en OCaml, car de sa matrise dpent la scalabilit de vos programmes. Voici un exemple de fonction rcursive qui calcule la somme d'une suite d'entier de 1 jusqu' un nombre donn :
# let rec add x = if x <= 1

14 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

then 1 else x + add (x - 1) ;; val add : int -> int = <fun>

priori, cette fonction parat correcte, sauf que pour des valeurs leves il risque d'y avoir une surcharge qui empchera cette fonction de renvoyer le rsultat.
# add 10 ;; - : int = 55 # add 100 ;; - : int = 5050 # add 10000 ;; - : int = 50005000 # add 100000 ;; Stack overflow during evaluation (looping recursion?).

Voici ce qui se passe, la dernire ligne de la fonction, la suite du else, il y a une addition avec le rsultat d'un appel de fonction, donc le calcul ne peut tre ralis aussitt car il faut d'abord calculer le rsultat de cette fonction, le calcul est donc laiss en suspend en attendant. Or il y aurra autant de calcul laisss en suspend que d'appel total la fonction, ce nombre correspondant ici la valeur du paramtre. Donc pour cent mille, il y aurra cent mille appels successifs la fonction et donc cent mille calculs laisss en instance ! La pile d'appel des fonctions (stack) est alors surcharge (overflow). C'est un peu comme une personne qui aurait du travail faire et qui chaque jour le remettrait au lendemain, et la fin il se retrouverait incapable de raliser tout le travail accumul. La solution est donc de raliser le calcul faire chaque tape et de passer le rsultat la boucle suivante. Il faut donc raliser une fonction auxiliaire 'aux' avec un paramtre additionnel 'acc' o le rsultat final s'accumule chaque boucle :
# let addt x = let rec aux x acc = if x <= 1 then acc else aux (x - 1) (x + acc) in aux x 1 ;; val addt : int -> int = <fun> # addt 100000 ;; - : int = 705082704

La nouvelle fonction est alors dite "rcursive terminale", car dans la rcursivit le rsultat est donn en position terminale, ici au niveau du 'then', alors qu'avant c'tait le bout de la chane le rsultat tant situ au niveau de l'addition (lors de la dernire boucle). Dans la premire version de la fonction, le then renvoyait un 1, c'est donc avec cette valeur que la variable acc est initialise lors de l'appel la fonction auxiliaire aux.

Voici un autre exemple de fonction rcursive non terminale. Cette fonction produit une liste d'entiers entre deux entiers donns :

15 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

# let rec up_to m n = if m > n then up_to n m else if m = n then [m] else m :: up_to (m+1) n ;; val up_to : int -> int -> int list = <fun> # up_to 4 12;; - : int list = [4; 5; 6; 7; 8; 9; 10; 11; 12]

Et voici la version rcursive terminale :


# let up_to m n = let rec aux m n acc = if m >= n then n::acc else aux m (n-1) (n::acc) in if m > n then aux n m [] else aux m n [] ;; val up_to : int -> int -> int list = <fun> # up_to 4 12 ;; - : int list = [4; 5; 6; 7; 8; 9; 10; 11; 12]

On peut vrifier la capacit de ces deux fonctions avec


ignore(up_to 0 100000) ;;

la version rcursive terminale est effectivement plus performante. D'aprs le manuel d'OCaml, il faut utiliser une version rcursive terminale ds lors que le nombre d'lments des listes traiter peut dpasser les dix milles (environ).

Script et excutables
Aprs avoir utilis le top-level, maintenant voyons comment crer des scripts et des excutables. Pour cel ouvrez un nouveau fichier avec l'extension de fichier .ml correspondant aux fichiers source OCaml, et crivez une simple instruction, comme par exemple :
print_endline "Hello World!" ;;

Pour excuter ce fichier en tant que script, il suffit ensuite de lancer l'interprteur en ligne de commande avec le script en argument comme ceci :
ocaml hello.ml

Si vous utilisez un systme de type Unix comme Linux ou MacOS, il vous est galement possible de pouvoir excuter votre script comme un excutable sans prciser l'interprteur sur la ligne de commande. Voici comment procder, premirement ajoutez la premire ligne du script le chemin de l'interprteur comme ceci :
#! /usr/bin/env ocaml

ensuite rendez le excutable en lui ajoutant les droits d'excution :


chmod a+x hello.ml

vous pouvez alors excuter directement votre script comme n'importe quel excutable :
./hello.ml

16 sur 17

06/07/2011 16:00

Tutoriel pour OCaml

http://www.linux-nantes.org/~fmonnier/ocaml/didacticiel-ocaml.php

Et pour ce qui est de crer des excutables pour de meilleures performences, vous pouvez compiler votre fichier source au choix en code natif ou en byte-code. La compilation en byte-code a pour avantage que l'excutable pourra tre excut indpendament sur n'importe quel systme d'exploitation o la machine virtuelle OCaml est installe. La machine virtuelle est un interprteur qui interprte le code binaire dans lequel a t compil le fichier source. La compilation en code natif a elle pour avantage les performences optimums (comparable du C ou du C++). Ainsi pour compiler en byte-code, utilisez le compilateur correspondant :
ocamlc hello.ml ./hello.bin -o hello.bin

Et de mme pour compiler un excutable en code natif :


ocamlopt ./hello hello.ml -o hello

Idem pour les utilisateurs de Windows ncessitant une extention de fichier :


ocamlopt hello.ml ./hello.exe -o hello.exe

Voil pour un premier pisode, il est un peu long et peu amusant pour un dbut, mais il faux comprendre le typage des expressions avant de pouvoir aller plus loin vers des choses plus volues. Dites-moi si vous l'avez apprci ou pas, pour que je sache si je dois en faire d'autres ou pas.

Camellement vtre ! GNU/FDL 2005 2006 2007 Florent Monnier

17 sur 17

06/07/2011 16:00

You might also like