> Introduction au langage OCaml : Développer en OCaml

Maxence Guesdon - INRIA Saclay Île-de-France - Durée: environ 2h

Plan

Introduction

Présentation du langage

Caractéristiques

OCaml est:

1

3 modes d'exécution

2

Extensions

Figure 1: Principales extensions des noms de fichiers utilisées par les compilateurs OCaml.
.ml Source d'implémentation (équivalent des .c en C)
.mli Source d'interface (cf. chapitre Programmation modulaire, sorte d'équivalent des .h en C)
.cmo Implémentation compilée en code-octet
.cmi Interface compilée
.cmx et .o Implémentation compilée en code natif
.cma Bibliothèque (regroupement de plusieurs implémentations compilées) en code-octet
.cmxa et .a Bibliothèque en code natif
.cmxs Bibliothèque en code natif compilée pour le chargement dynamique.
Figure 1
Programmation fonctionnelle

Types de base

Entiers

# 1 (* l'entier 1 *);;
- : int = 1
# 1 + 0xde (* l'addition avec l'opérateur '+', 0xde est la notation hexadécimale*) ;;
- : int = 223
# 3 - 0o11 (* la soustraction, 0o11 est la notation octale *) ;;
- : int = -6
# 4 * 0b10101 (* la multiplication, 0b10101 est la notation binaire *) ;;
- : int = 84
# 10 / 3 (* la division entière *) ;;
- : int = 3
# (/) 10 3 (* idem, en utilisant l'opérateur en position préfixe *) ;;
- : int = 3
# ( * ) 10 3 (* multiplication; espaces nécessaires pour distinguer des commentaires *);;
- : int = 30
# 15000003 mod 4 (* le modulo, reste de la division entière *) ;;
- : int = 3
# 15_000_003 mod 4 (* même opération, avec un entier plus lisible *) ;;
- : int = 3
# max_int (* le plus grand entier disponible *) ;;
- : int = 4611686018427387903
# min_int - 1 (* attention, pas de contrôle sur le dépassement des limites *) ;;
- : int = 4611686018427387903
1.1.1

Types de base

Flottants

# 1.0 (* le réel 1.0 *);;
- : float = 1.
# 1.0e+5 (* notation avec exposant *) ;;
- : float = 100000.
# 1.2 +. 1. (* l'addition avec l'opérateur '+.' *) ;;
- : float = 2.2
# 3.3 -. 2.15 (* la soustraction *) ;;
- : float = 1.15
# 4. *. 3. (* la multiplication *) ;;
- : float = 12.
# 10. /. 3. (* la division *) ;;
- : float = 3.33333333333333348
# (/.) 10. 3. (* idem, en utilisant l'opérateur en position préfixe *) ;;
- : float = 3.33333333333333348
# mod_float 15000003. 4.5 (* le modulo *) ;;
- : float = 0.
# mod_float 15_000_003. 4.5 (* même opération, avec un flottant plus lisible *) ;;
- : float = 0.
# 1.999999999 +. 100000000. (* la précision est celle de la norme IEEE 754 *);;
- : float = 100000002.
# ceil 3.5 (* arrondi à l'entier supérieur *) ;;
- : float = 4.
# floor 3.5 (* arrondi à l'entier inférieur *) ;;
- : float = 3.
# truncate 3.5 (* ne conserve que la partie entière *) ;;
- : int = 3
# 1. /. 0. (* la division par zéro donne l'infini *);;
- : float = infinity
# 0. /. 0. (* diviser 0 par 0 ne donne pas un nombre, 'not a number' ou 'NaN' *);;
- : float = nan
1.1.1.2

Types de base

Booléens

# true (* la valeur 'vrai' *) ;;
- : bool = true
# false (* la valeur 'faux' *) ;;
- : bool = false
# true && false (* ET paresseux séquentiel, évalué de gauche à droite *) ;;
- : bool = false
# true || false (* OU paresseux séquentiel, évalué de gauche à droite *) ;;
- : bool = true
# not true (* NON logique *) ;;
- : bool = false
1.1.1.3

Types de base

Caractères

# 'a' (* le caractère 'a' *) ;;
- : char = 'a'
# int_of_char 'a' (* son code ASCII *) ;;
- : int = 97
# char_of_int 123 (* le caractère de code ASCII 123 *) ;;
- : char = '{'
1.1.1.4

Types de base

Chaînes de caractères

# String.length "hello world!" (* longueur de chaîne *);;
- : int = 12
# String.sub "hello world!" 0 5 ;;
- : string = "hello"
1.1.1.5

Types de base

Unit

# ();;
- : unit = ()
1.1.1.6

Types de base

Tuples

# (1, 2) (* paire d'entiers *) ;;
- : int * int = (1, 2)
# (1, "2") (* paire composée d'un entier et d'une chaîne *) ;;
- : int * string = (1, "2")
# fst (1, 2) (* accès à la première composante de la paire *) ;;
- : int = 1
# snd (1, 2) (* accès à la seconde composante *);;
- : int = 2
# (1, 2, 3) (* n-uplet de 3 éléments *) ;;
- : int * int * int = (1, 2, 3)
# (1.0, "hello", "world", '!') (* n-uplet de 4 éléments de 3 types différents *) ;;
- : float * string * string * char = (1., "hello", "world", '!')
1.1.1.7

Types de base

Listes

# [] (* une liste vide, donc sans contrainte sur le type des éléments *) ;;
- : 'a list = []
# [ 1 ] (* une liste d'entiers à un élément *) ;;
- : int list = [1]
# [ 1 ; 2 ; 3 ] (* une liste de 3 entiers *) ;;
- : int list = [1; 2; 3]
# [ "hello" ; "world" ; "!" ] (* une liste de chaînes *) ;;
- : string list = ["hello"; "world"; "!"]
# [ 1 ; "2" ; 3 ] (* liste invalide car les éléments n'ont pas le même type *) ;;
Error: This expression has type string but an expression was expected of type
         int
1.1.1.8

Types de base

Attention

# 1 +. 2.0 ;;
Error: This expression has type int but an expression was expected of type
         float
# 1 + 2.0;;
Error: This expression has type float but an expression was expected of type
         int

Types de base

Comparaisons

# 1 = 2 (* égalité structurelle sur des entiers *) ;;
- : bool = false
# "bonjour" = "monde" (* ... ou sur des chaînes *) ;;
- : bool = false
# (1, 2, 3) = (1, 2, 4) (* ... ou des n-uplets *) ;;
- : bool = false
# [ 1 ; 2 ; 3 ] <> [ 1 ; 2 ; 4 ] (* différence structurelle, ici sur des listes *) ;;
- : bool = true
# not ([ 1 ; 2 ; 3 ] = [ 1 ; 2 ; 4 ])
  (* idem que ci-dessus, en utilisant la négation de l'égalité *) ;;
- : bool = true
# 1 < 2 (* infériorité (structurelle) stricte *) ;;
- : bool = true
# 1 <= 2 (* inférieur ou égal *) ;;
- : bool = true
# "bonjour" > "monde" (* supérieur *) ;;
- : bool = false
# [ 1 ; 2 ; 3 ] >= [ 1 ; 2 ] (* supérieur ou egal *) ;;
- : bool = true
# 1 == 2 (* égalité physique *) ;;
- : bool = false
# 1 != 2 (* différence physique *) ;;
- : bool = true
1.2

Conditionnelle

if expression1 then expression2 else expression3
if expression1 then expression2
# if true then 1 lsl 5 + 10 else 1
  (* lsl est le décalage logique de bits vers la gauche *) ;;
- : int = 42
# (if 12 * 4 < 40 then 15 else 2) + (if 3 > 4 then 10 else 4 * 10)
  (* une expression conditionnelle est aussi une expression dont le calcul
     retourne une valeur. *);;
- : int = 42
# if true then 1;;
Error: This expression has type int but an expression was expected of type
         unit
1.3

Déclarations

Déclaration globale:

let ident = expression;;
# let x = 1 ;;
val x : int = 1
# let y = 41 ;;
val y : int = 41
# let z = x + y ;;
val z : int = 42

Déclaration locale:

let ident = expression1 in expression2
# let foo = (* déclaration globale foo *)
    let bar =
      let glop = 1 in (* déclaration locale de glop *)
        glop + 2
    in (* déclaration locale de bar, glop n'est plus visible *)
      let gee = bar + 1 in (* déclaration locale de gee, utilisant bar *)
        gee + bar + 2 (* utilisation de gee et bar locales *);;
val foo : int = 9
# let buz = bar + foo (* bar n'est plus visible ici *) ;;
Error: Unbound value bar
1.4

Déclarations simultanées

# let x_int = 1
and x_float = 1.0;;
val x_int : int = 1
val x_float : float = 1.
# let y_int = 2
and y_float = float_of_int y_int;;
Error: Unbound value y_int
Hint: Did you mean x_int?
# let (a, b, c) = (1, 2, 3);;
val a : int = 1
val b : int = 2
val c : int = 3
# let (a, b) = (1, 2, 3);;
Error: This expression has type 'a * 'b * 'c
       but an expression was expected of type 'd * 'e
# let (a, b, c) = (1, 2);;
Error: This expression has type 'a * 'b
       but an expression was expected of type 'c * 'd * 'e
1.4.3

Fonctions

Définition:

# function x -> x * x (* la fonction carré sur les entiers *) ;;
- : int -> int = <fun>
# function x -> function y -> x +. y (* l'addition sur les flottants *) ;;
- : float -> float -> float = <fun>
# fun x y -> x +. y (* raccourci syntaxique pour l'expression ci-dessus *);;
- : float -> float -> float = <fun>

Application:

# (function x -> x + 1) 41 ;;
- : int = 42
# ((function x -> function y -> x +. y) 1.0) 41.0 ;;
- : float = 42.
# (function x -> function y -> x +. y) 1.0 41.0 (* suppression des parenthèses inutiles *) ;;
- : float = 42.

Application partielle:

# (function x -> function y -> x +. y) 1.0;;
- : float -> float = <fun>
1.5

Nommage de fonctions

# let square = function x -> x * x (* fonction carré sur les entiers *) ;;
val square : int -> int = <fun>
# let add = fun x y -> x + y (* en utilisant le raccourci de syntaxe *) ;;
val add : int -> int -> int = <fun>
# let sub = function x -> function y -> x - y ;;
val sub : int -> int -> int = <fun>
# let sub x y = x - y (* idem en utilisant un autre raccourci de syntaxe *) ;;
val sub : int -> int -> int = <fun>

Utilisation:

# square 24 ;;
- : int = 576
# square 24.0 (* erreur: le paramètre doit être un entier *) ;;
Error: This expression has type float but an expression was expected of type
         int
# (square 1) 2 (* erreur (square 1) n'est pas une fonction et ne peut être appliquée *) ;;
Error: This expression has type int
       This is not a function; it cannot be applied.
# let add1 = add 1 (* application "partielle" de add pour obtenir une fonction d'incrément *) ;;
val add1 : int -> int = <fun>
# add1 41 ;;
- : int = 42
1.5

Place des parenthèses

# let f x y = x + y ;;
val f : int -> int -> int = <fun>
# f 1 2 ;;
- : int = 3
# (f 1) 2 ;;
- : int = 3
# (f 1 2) ;;
- : int = 3
# ((f 1) 2) ;;
- : int = 3
# f ( 1 ) 2 ;;
- : int = 3
# f 1 ( 1 + 1);;
- : int = 3
# f ( 0 + 1 ) ( 2 + 0 );;
- : int = 3
# f (1 2);;
Error: This expression has type int
       This is not a function; it cannot be applied.
Avertissement 5

Fermetures

# let increment = 2;;
val increment : int = 2
# let add_increment x = x + increment ;;
val add_increment : int -> int = <fun>
# add_increment 1 ;;
- : int = 3
# let increment = 20 ;;
val increment : int = 20
# add_increment 1 ;;
- : int = 3
1.5.3

Fonctions d'ordre supérieur

# let h = function f -> function y -> (f y) + y ;;
val h : (int -> int) -> int -> int = <fun>
# List.map ;;
- : ('a -> 'b) -> 'a list -> 'b list = <fun>
# List.map square [ 1 ; 2 ; 3 ; 4 ; 5 ; 6 ];;
- : int list = [1; 4; 9; 16; 25; 36]
1.5.5

Récursion

# let compte x = if x <= 0 then x else compte (x-1) + 1;;
Error: Unbound value compte
Hint: Did you mean compare?
# let rec compte x = if x <= 0 then x else compte (x-1) + 1;;
val compte : int -> int = <fun>
# compte 10;;
- : int = 10
# let rec triple x = if x <= 0 then 0 else triple_aux (x - 2) + 1
and triple_aux x = triple (x+1) + 2;;
val triple : int -> int = <fun>
val triple_aux : int -> int = <fun>
# triple 20;;
- : int = 60
1.6

Exercice

Exercice 1: Redéfinition de List.map

Réécrire la fonction List.map, prenant en paramètres une fonction f et une liste d'éléments et retournant une nouvelle liste, résultat de l'application de f à chaque élément. La fonction conserve l'ordre des éléments. On utilisera la récursion et les fonctions List.length, List.tl, List.hd et le constructeur ::.

Exercice 1

Exercice

Exercice 2: Le plus petit entier

Ecrire une fonction prenant une liste d'entiers et retournant le plus petit ou max_int si la liste est vide.

Exercice 2

Exercice

Exercice 3: Moyenne d'une liste

Ecrire une fonction prenant une liste d'entiers et retournant la moyenne sous forme d'un flottant, ou 0. si la liste est vide.

Exercice 3

Filtrage de motif (pattern matching)

La syntaxe est la suivante:

match expression_0 with
 | pattern_1 -> expression_1
 | pattern_2 -> expression_2
 | ...

Filtrage d'un entier:

# match 1 + 1 with
  0 -> print_endline "bizarre"
| 1 -> print_endline "étrange"
| 2 -> print_endline "ouf!"
;;
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
3
ouf!
- : unit = ()
2.1

Filtrage de motif (pattern matching)

Filtrage d'une liste:

# match List.map string_of_int [ 1 ; 2 ; 3 ] with
  []
| _ :: [] -> assert false
| [s1 ; s2] -> assert false
| s1 :: s2 :: s3 :: _ ->
    List.iter print_endline [ s1 ; s2 ; s3 ]
| l ->
    List.iter print_endline l
;;
Warning 11: this match case is unused.
1
2
3
- : unit = ()

Attention:

# match [ 0 ] with
| [x] when x = x -> true
| _ -> false
;;
- : bool = true
2.1

Filtrage de motif (pattern matching)

# let (x, y, z) = (1, "2", 3.0);;
val x : int = 1
val y : string = "2"
val z : float = 3.
# let [s1 ; s2 ; s3] = [ "un" ; "deux" ; "trois"];;
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
(_::_::_::_::_|_::_::[]|_::[]|[])
val s1 : string = "un"
val s2 : string = "deux"
val s3 : string = "trois"
# let [x1 ; x2] = [ 1 ; 2 ; 3 ] ;;
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
(_::_::_::_|_::[]|[])
Exception: Match_failure ("", 1, 4).
2.3

Exercice

Exercice 4: Liste palindrome

Définir une fonction prenant en paramètre une liste d'éléments quelconques et retournant vrai si la liste est un palindrome, faux sinon.

Quelques tests:

# palindrome_list [1 ; 2 ; 3 ; 2 ; 1 ];;
- : bool = true
# palindrome_list ['1' ; '2' ; '3' ; '3' ; '2' ; '1' ];;
- : bool = true
# palindrome_list ['r' ; 'a' ; 'd' ; 'a'; 'r'];;
- : bool = true
# palindrome_list ['r' ; 'a' ; 'v' ; 'e'; 'r'];;
- : bool = false
# palindrome_list [1.0 ; 2.0 ; 3.0 ; 4.0 ; 2.0 ; 1.0];;
- : bool = false
# palindrome_list [];;
- : bool = true
Exercice 4

Déclaration de types

# type 'a pairs = ('a * 'a) list;;
type 'a pairs = ('a * 'a) list
# let int_pair_list = [ (1, 2) ; (5, 4) ] ;;
val int_pair_list : (int * int) list = [(1, 2); (5, 4)]
# let (int_pairs : int pairs) = [ (1, 2) ; (3, -3) ; (6, 8)];;
val int_pairs : int pairs = [(1, 2); (3, -3); (6, 8)]
# let (bool_pairs : bool pairs) = [ (true, true) ; (false, false) ; (true, false)];;
val bool_pairs : bool pairs = [(true, true); (false, false); (true, false)]
# (=);;
- : 'a -> 'a -> bool = <fun>
# int_pair_list;;
- : (int * int) list = [(1, 2); (5, 4)]
# int_pairs;;
- : int pairs = [(1, 2); (3, -3); (6, 8)]
# int_pair_list = int_pairs;;
- : bool = false
# let (float_pairs : float pairs) = [ (1., 2.) ; (3., 4.)];;
val float_pairs : float pairs = [(1., 2.); (3., 4.)]
# int_pairs = float_pairs;;
Error: This expression has type float pairs = (float * float) list
       but an expression was expected of type int pairs = (int * int) list
       Type float is not compatible with type int
2.4

Types enregistrements (records)

# type coord = { x : float ; y : float};;
type coord = { x : float; y : float; }
# let origin = { x = 0. ; y = 0. };;
val origin : coord = {x = 0.; y = 0.}
# let add p1 p2 = { x = p1.x +. p2.x ; y = p1.y +. p2.y };;
val add : coord -> coord -> coord = <fun>
# let unite = { x = 1. ; y = 1. };;
val unite : coord = {x = 1.; y = 1.}
# let unite_x = { unite with y = 0. };;
val unite_x : coord = {x = 1.; y = 0.}
# let p1 = add (add origin unite) unite;;
val p1 : coord = {x = 2.; y = 2.}
# p1 = unite;;
- : bool = false
# match add p1 unite with
  { x = mon_x } as foo when foo.y > 1. -> string_of_float mon_x
| { x = x ; y = y } -> string_of_float (x +. y);;
- : string = "3."
2.5

Types sommes (variants)

# type direction =
  Nord
| Sud
| Est
| Ouest
| Compose of direction * direction;;
type direction = Nord | Sud | Est | Ouest | Compose of direction * direction

Création de valeur:

# let nne = Compose (Nord, Compose (Nord, Est));;
val nne : direction = Compose (Nord, Compose (Nord, Est))

Filtrage de valeur:

# let rec string_of_direction = function
  Nord -> "nord"
| Sud -> "sud"
| Est -> "est"
| Ouest -> "ouest"
| Compose (d1, d2) -> (string_of_direction d1)^"-"^(string_of_direction d2);;
val string_of_direction : direction -> string = <fun>
# string_of_direction nne;;
- : string = "nord-nord-est"
2.6

Exercice: représentation d'expressions

Exercice 5: Représentation d'expressions arithmétiques

Définir un type somme expr pour représenter les expressions contenant les 4 opérations habituelles sur les entiers ainsi que les valeurs entières. On aura donc 5 constructeurs.

Ensuite, définir une fonction prenant une valeur de ce type et calculant la valeur entière en réalisant l'opération décrite.

2.6.4
Utiliser la bibliothèque standard

La bibliothèque standard

Elle comporte

Des bibliothèques additionnelles sont disponibles selon les systèmes: Unix, Dynlink, Str, Threads, ...

Bibliothèques

Utiliser un module

Deux façons de faire référence à un élément d'un module:

Attention au masquage:

# let length x = x + 1;;
val length : int -> int = <fun>
# length 10;;
- : int = 11
# open List;;
# length 10;;
Error: This expression has type int but an expression was expected of type
         'a list

Exceptions

try ...
with
  Exc1 -> ...
| Exc2 -> ...
Exemple:
# let first_line file =
  try
    let ic = open_in file in
    try let line = input_line ic in close_in ic; line
    with End_of_file -> close_in ic; ""
  with
    Sys_error msg ->
      prerr_endline msg;
      ""
;;
val first_line : string -> string = <fun>
# first_line "/etc/passwd";;
- : string = "root:x:0:0:root:/root:/bin/bash"
# first_line "/foo/bar";;
/foo/bar: No such file or directory
- : string = ""
Des exceptions sont prédéfinies dans le module Pervasives.
# exception Mon_exception of string * int ;;
exception Mon_exception of string * int

Exercice

type 'a option = None | Some of 'a
Exercice 4: Tac

Ecrire une fonction tac qui lit un fichier en paramètre et retourne la liste de ses lignes en ordre inverse.

Exemple d'appel:

# tac "/etc/shells";;
/usr/bin/zsh
/bin/zsh
/bin/rbash
/bin/bash
/bin/dash
/bin/sh
# /etc/shells: valid login shells
- : unit = ()
Exercice 4

Exercice

Exercice 6: Découpage en liste de mots

Ecrire une fonction prenant une chaîne de caractères et retournant la liste des mots qu'elle contient. Un mot sera composé uniquement des caractères 'a' à 'z' et 'A' à 'Z'.

Note: Il est possible de filtrer un caractère selon un intervalle de caractères donné, grâce à la notation ..:

# match 'c' with
  'a'..'z'
| 'A'..'Z' -> "letter"
| '0'..'9' -> "number"
| _ -> "something else"
;;
- : string = "letter"
# words "maitre corbeau sur un arbre perche ... par l'odeur alleche...";;
- : string list =
["maitre"; "corbeau"; "sur"; "un"; "arbre"; "perche"; "par"; "l"; "odeur";
 "alleche"]
Exercice 6

Ce que nous n'avons pas vu

TryOcaml

_
TryOCaml (thanks to OCamlPro)
 Welcome to TryOCaml
#