Programmation fonctionnelle avanc ´ ee Notes de cours Remise ` a niveau 5 septembre 2017 Sylvain Conchon [email protected] 1/103
Programmation fonctionnelleavancee
Notes de cours
Remise a niveau
5 septembre 2017
Sylvain Conchon
1/103 1
Structure du cours
Le cours est a la fois :
I Une introduction a la programmation fonctionnelleI Une initiation au langage OCaml
2/103 2
Un livre qui peut vous aider (Amazon, FNAC, etc.)
http://programmer-avec-ocaml.lri.fr/
978
2212
1367
84
3/103 3
Environnements de travail
4/103 4
Le langage OCaml
http://caml.inria.fr
I langage developpe a l’INRIA (Institut National de Rechercheen Informatique et en Automatique)
I disponible sur de nombreuses architectures (Linux, Windows,Mac OS X etc.)
5/103 5
Les outils
Nous allons utiliser les outils suivants en TP :
I un terminal unixI un editeur de texte : emacsI un interpreteur de commandes OCaml : ocamlI un compilateur pour OCaml : ocamlc
6/103 6
Demonstration
Le premier programme :
afficher Hello world! a l’ecran
7/103 7
L’interpreteur : lancement
Tres pratique pour ecrire de petits programmes ou connaıtre lavaleur (ou le type) d’une expression
Pour cela, dans la terminal je tape simplement ocaml
> ocaml
OCaml version 4.01.0
#
Le symbole # est l’invite de commande de l’interpreteur : il vousinvite a ecrire une expression OCaml
8/103 8
L’interpreteur : evaluation d’une expression
L’evaluation d’une expression se fait en trois temps
> ocaml
OCaml version 4.01.0
#
1 + 2 ;;
- : int = 3
#
1. Je saisie l’expression et j’indique que j’ai termine en tapant ;;
2. L’interpreteur evalue mon expression
3. Puis il affiche son type et sa valeur
9/103 9
L’interpreteur : evaluation d’une expression
L’evaluation d’une expression se fait en trois temps
> ocaml
OCaml version 4.01.0
# 1 + 2 ;;
- : int = 3
#
1. Je saisie l’expression et j’indique que j’ai termine en tapant ;;
2. L’interpreteur evalue mon expression
3. Puis il affiche son type et sa valeur
9/103 9
L’interpreteur : evaluation d’une expression
L’evaluation d’une expression se fait en trois temps
> ocaml
OCaml version 4.01.0
# 1 + 2 ;;
- : int = 3
#
1. Je saisie l’expression et j’indique que j’ai termine en tapant ;;
2. L’interpreteur evalue mon expression
3. Puis il affiche son type et sa valeur
9/103 9
L’interpreteur : evaluation d’une expression
L’evaluation d’une expression se fait en trois temps
> ocaml
OCaml version 4.01.0
# 1 + 2 ;;
- : int = 3
#
1. Je saisie l’expression et j’indique que j’ai termine en tapant ;;
2. L’interpreteur evalue mon expression
3. Puis il affiche son type et sa valeur
9/103 9
L’interpreteur : evaluation d’une expression
L’evaluation d’une expression se fait en trois temps
> ocaml
OCaml version 4.01.0
# 1 + 2 ;;
- : int = 3
#
1. Je saisie l’expression et j’indique que j’ai termine en tapant ;;
2. L’interpreteur evalue mon expression
3. Puis il affiche son type et sa valeur
9/103 9
Le compilateur : edition, compilation, execution
L’ecriture de (gros) programmes necessite un editeur et uncompilateur
Le cycle d’utilisation du compilateur est egalement en trois temps
1. J’ecris mon programme hello.ml a l’aide d’emacs
2. Dans le terminal, je compile
> ocamlc -o hello hello.ml
>
3. J’execute mon programme
> ./hello
Hello wolrd!
10/103 10
Le compilateur : edition, compilation, execution
L’ecriture de (gros) programmes necessite un editeur et uncompilateur
Le cycle d’utilisation du compilateur est egalement en trois temps
1. J’ecris mon programme hello.ml a l’aide d’emacs
2. Dans le terminal, je compile
> ocamlc -o hello hello.ml
>
3. J’execute mon programme
> ./hello
Hello wolrd!
10/103 10
Le compilateur : edition, compilation, execution
L’ecriture de (gros) programmes necessite un editeur et uncompilateur
Le cycle d’utilisation du compilateur est egalement en trois temps
1. J’ecris mon programme hello.ml a l’aide d’emacs
2. Dans le terminal, je compile
> ocamlc -o hello hello.ml
>
3. J’execute mon programme
> ./hello
Hello wolrd!
10/103 10
Le compilateur : edition, compilation, execution
L’ecriture de (gros) programmes necessite un editeur et uncompilateur
Le cycle d’utilisation du compilateur est egalement en trois temps
1. J’ecris mon programme hello.ml a l’aide d’emacs
2. Dans le terminal, je compile
> ocamlc -o hello hello.ml
>
3. J’execute mon programme
> ./hello
Hello wolrd!
10/103 10
Programme 1 : Annees bissextiles
Notions introduites :
I forme generale d’un programmeI types de base (int, bool, string, unit)I construction let
I appel de fonctionI fonction d’affichage Printf.printf
11/103 11
La forme des programmes Ocaml
Un programme OCaml est simplement :
I une suite de declarations (let) ou d’expressions a evaluer(de haut en bas)
I la fin d’une declaration ou d’une expression est specifiee pardeux points virgules ;;
Il n’y a donc pas de point d’entree particulier (fonctionprincipale par ex.) comme dans d’autres langages.
12/103 12
Types et expressions elementaires
Expressions de type int : les entiers
# 4 + 1 - 2 * 2 ;;
- : int = 1
# 5 / 2 ;;
- : int = 2
# 1 000 005 mod 2 ;;
- : int = 1
# max int + 1 ;;
- : int = -1073741824
I int represente les entiers compris entre −230 et 230 − 1 (surune machine 32 bits)
I operations sur ce type : +, -, *, / (division entiere), mod (restede la division) etc.
13/103 13
Types et expressions elementaires
Expressions de type bool : les valeurs booleennes
# false || true ;;
- : bool = true
# 3 <= 1 ;;
- : bool = false
# not (0=2) && 1>=3 ;;
- : bool = false
# if 2<0 then 2.0 else (4.6 *. 1.2) ;;
- : float = 5.52
I les constantes true (vrai) et false (faux)I les operations sur ce type not (non), && (et) et || (ou)I les operateurs de comparaison (=, <, >, <=, >=) retournent des
valeurs booleennesI dans une conditionnelle de la forme
if exp1 then exp2 else exp3l’expression exp1 doit etre de type bool.
14/103 14
Types et expressions elementaires
Expressions de type char : les caracteres
# ’a’ ;;
- : char = ’a’
# int of char ’a’ ;;
- : int = 97
# char of int 100 ;;
- : char = ’d’
I les caracteres sont encadres par deux apostrophes ’
I la fonction int of char renvoie le code ASCII d’un caractere(et inversement pour la fonction char of int).
15/103 15
Types et expressions elementaires
Expressions de type string : les chaınes de caracteres
# "hello" ;;
- : string = "hello"
# "" ;;
- : string = ""
# "bon" ^ "jour" ;;
- : string = "bonjour"
# "hello".[1] ;;
- : char = ’e’ # string of int 123 ;;
- : string = "123"
I ces valeurs sont encadrees par deux guillemets "I l’operateur ^ concatene des chaınesI l’operation .[i] accede au ie caractere d’une chaıne (le
premier caractere est a l’indice 0)I des fonctions de conversions permettent de convertir des
valeurs de types de base en chaınes (et inversement)16/103 16
Le type unit
# () ;;
- : unit = ()
# Print.printf "bonjour\n" ;;bonjour
- : unit = ()
I unit represente les expressions qui font uniquement deseffets de bord
I une seule valeur a ce type, elle est notee ()
I c’est l’equivalent du type void en C
17/103 17
Les variables globales
# let x = 3 ;;
val x : int = 3
I le type est infere automatiquement par le compilateurI le contenu d’une variable n’est pas modifiableI la portee est limitee aux declarations suivantes
# let y = 5 + x ;;
val y : int = 8
# let z = 10 + z ;;
Error: Unbound value z
I La liaison est statique : la redefinition ne change pas la valeurdes expressions precedentes
# let x = 10 ;;
val x : int = 10
# y ;;
- : int = 8
18/103 18
Appel de fonction
L’appel d’une fonction toto avec un argument v se notesimplement :
toto v
il n’y a donc pas de parentheses
Si la fonction toto a plusieurs arguments, par exemple troisarguments, on note simplement :
toto v1 v2 v3
19/103 19
La fonction Printf.printf
(Il s’agit d’une fonction tres connues des programmeurs C)
Printf.printf prend comme premier argument une chaıne deformatage qui contient le message a ecrire
Ce message peut contenir des codes de format (commencant par%) qui indiquent qu’un argument est attendu a cet endroit
Selon le code de format, le type de l’argument est :
%d un entier%c un caractere%f un flottant%s une chaıne de caracteres
La fonction Print.printf attend donc autant d’arguments quenecessaire pour construire le message a afficher
20/103 20
Programme 2 : Methode de Monte-Carlo
Notions introduites :
I nombres flottants (type float)I variables locales (construction let-in)I bibliotheque de nombres aleatoires Random
I acces aux arguments d’un programme (Sys.argv)I declaration de fonctionsI fonctions recursivesI fonctions a plusieurs arguments
21/103 21
Calcul de π par la methode de Monte-Carlo
I soit un carre de cote 1 et le quart de cercle de rayon 1 inscritdans ce carre (l’aire de ce cercle est π/4)
I si l’on choisit au hasard un point du carre, la probabilite qu’ilsoit dans le quart de cercle est donc egalement de π/4
I en tirant au hasard un grand nombre n de points dans lecarre, si p est le nombre de points a l’interieur du cercle, alors4× p/n donne une bonne approximation de π
22/103 22
Le type float
Expressions de type float : les nombres a virgule flottante
# 4.3e4 +. 1.2 *. -2.3 ;;
- : float = 42997.24
# 5. /. 2. ;;
- : float = 2.5
# 1. /. 0. ;;
- : float = infinity
# 0. /. 0. ;;
- : float = nan
I les types int et float sont disjointsI operations sur ce type : +. -. *. /. sqrt cos etc.I on passe d’un entier a un flottant a l’aide de la fonction
float of int et inversement avec truncate
23/103 23
Les variables locales
# let x = 3 in x + 1;;
- : int = 4
I la portee est limitee a l’expression qui suit le in
# x + 2;;
Error: Unbound value x
I le nom de la variable locale masque toute declarationanterieure de meme nom
# let y = 2;;
val y : int = 2
# let y = 100.5 in (truncate y) + 1;;
- : int = 101
# y + 3;;
- : int = 5
24/103 24
La bibliotheque Random
Cette bibliotheque contient plusieurs fonctions pour generer desnombres aleatoires
Random.float n renvoie, de maniere aleatoire, un nombre flottantentre 0 et n (inclus) (si n est negatif, le resultat est negatif ou 0)
Il existe d’autres fonctions comme Random.int (pour generer desentiers), Random.bool, etc.
Il faut appeler la fonction Random.self init () pour initialiser legenerateur (sans quoi on obtient toujours la meme sequence pourchaque execution)
25/103 25
Les arguments d’un programme
L’acces aux arguments d’un programme (passes sur la ligne decommande dans le terminal) se fait a l’aide de la notation
Sys.argv.(i)
ou i est un entier tel que :
O le nom du programme execute1 le premier argument2 le deuxieme argument. . .
Sys.argv.(i) est une chaıne de caracteres
Ainsi, si je tape dans le terminal
> ./approx_pi 100
Sys.argv.(0) vaut "./approx", et Sys.argv.(1) vaut "100"26/103 26
Fonctions
# let f x = x + 2;;
val f : int -> int = <fun>
# f 4;;
- : int = 6
I les types, des arguments et du resultat, sont inferesI la regle de portee du nom de la fonction est identique a celle
des constantes (globales ou locales)
# let h x = x / 2 in h 6;;
- : int = 3
# h 4;;
Error : Unbound value h
# let g x = x || g (not x);;
Error : Unbound value g
27/103 27
Fonctions a plusieurs arguments
# let f x y z =
if x then y + 1 else z - 1;;
val f : bool -> int -> int -> int = <fun>
# f true 2 3;;
- : int = 3
I les parametres ne sont pas entre parentheses, ni dans lesdeclarations, ni dans les applications de fonctions
28/103 28
Fonctions recursives
# let rec fact x =
if x <= 0 then 1
else x * fact (x - 1);;
val fact : int -> int = <fun>
# fact 4;;
- : int = 24
I l’ajout du mot-cle rec change la portee de l’identificateur : ilest alors accessible dans la definition de la fonction
29/103 29
Programme 3 : Dessin d’une cardioıde
Notions introduites :
I bibliotheque Graphics
I Sequence d’expressions
30/103 30
Definition d’une cardioıde
{x(θ) = a (1− sin(θ)) cos(θ)y(θ) = a (1− sin(θ)) sin(θ)
31/103 31
La bibliotheque Graphics
I ajouter le fichier graphics.cmxa dans la commande decompilation (ocamlopt ... graphics.cmxa ...)
I la directive open Graphics permet d’acceder directementaux fonctions de la bibliotheque Graphics
I open graph " 300x200" ouvre une fenetre graphique de300 pixels de large et 200 de haut
0 axe des x0
axe des y
x
y •
299
199
32/103 32
Quelques fonctions de Graphics
I plot x y affiche un pixel en (x, y) dans la couleur couranteI rgb r v b renvoie une couleur calculee a partir de
composantes rouge, vert et bleu (entiers entre 0 et 255) ; lescouleurs predefinies : white, black, blue, green etc.
I set color c fixe la couleur courante a la valeur cI let st = wait next event [Button down] attend un clic
de souris ; les coordonnees sont st.mouse x et st.mouse y
33/103 33
Sequences
Une sequence d’expressions permet d’evaluer des expressions lesunes apres les autres
# let x = Print.printf "bonjour\n"; 5 ;;
bonjour
val x : int = 5
I l’operateur de sequence est le point virgule ;I c’est un operateur binaire
Etant donnee une sequence d’expressions e1;e2;· · · ;en
I toutes les expressions e1, . . . , en−1 ne font que des effets debord
I la valeur et le type de la sequence sont ceux de la derniereexpression en
34/103 34
Programme 4 : La fractale de Mandelbrot
Notion introduite :
I fonctions locales
35/103 35
L’ensemble de Mandelbrot
Ensemble des points (a, b) du plan pour lesquels aucune des deuxsuites recurrentes suivantes ne tend vers l’infini (en valeurabsolue)
x0 = 0y0 = 0xn+1 = x2n − y2n + ayn+1 = 2xnyn + b
I pas de methode exacte pour determiner cette conditionI on peut demontrer que l’une de ces suites tend vers l’infini
des que x2n + y2n > 4
I les points sont dans le cercle de rayon 2 centre en (0, 0)
On dessine une approximation : ensemble des points (a, b) pourlesquels x2n + y2n ≤ 4 pour les k premieres valeurs de ces suites
36/103 36
les fonctions locales
Une declaration de fonction peut etre locale a une expression et oulocale a la declaration d’une autre fonction
Ainsi, le programme suivant :
let boucle n =
let rec blc_rec i =
Printf.printf "%d " i;
if i<n then blc_rec (i+1)
in
blc_rec 0;;
let carre x = x * x in boucle (carre 3);;
affiche 0 1 2 3 4 5 6 7 8 9
37/103 37
Definitions recursives
Une fonction est recursive si elle fait appel a elle meme dans sapropre definition
Par exemple, la fonction factorielle n! peut etre definie de maniererecursive, pour tout entier n, par les deux equations suivantes
0! = 1n! = n× (n− 1)!
38/103 38
Fonctions recursives en OCAML
La definition d’une fonction recursive est introduite par l’adjonctiondu mot cle rec au mot cle let
let rec fact n =
if n=0 then 1 else n * fact (n-1)
;;
39/103 39
Pourquoi ce mot cle rec?
La regle de portee du let
let f x = x + 1
let f y = if y=0 then f 2 else 4+y
40/103 40
Pourquoi ce mot cle rec?
La regle de portee du let
let f x = x + 1
let rec f y = if y=0 then f 2 else 4+y
40/103 40
Definitions par cas
Analyse par cas d’une valeur avec la construction match–with
let rec fact n =
match n with
| 0 -> 1
| _ -> n * fact (n-1)
;;
Ceci permet de se rapprocher encore plus de la definitionmathematique
41/103 41
Recursivite double
Recursivite double : plusieurs appels recursifs peuvent apparaıtredans la definition
let rec fibonacci n =
match n with
| 0 -> 0
| 1 -> 1
| _ -> fibonacci (n-1) + fibonacci (n-2)
;;
42/103 42
Recursivite double
Recursivite double : plusieurs appels recursifs peuvent apparaıtredans la definition
let rec fibonacci n =
match n with
| 0 -> 0
| 1 -> 1
| _ -> fibonacci (n-1) + fibonacci (n-2)
;;
42/103 42
Recursivite mutuelle (1/2)
Recursivite mutuelle : une fonction f peut faire reference a unefonction g qui elle-meme fait reference a f
Dans un fichier pair.ml :
let rec pair n = (n = 0) || impair (n-1) ;;
let rec impair n = (n <> 0) && pair (n-1) ;;
compilation
> ocamlc -o pair pair.ml
File "pair.ml", line 1, characters 28-34:
Unbound value impair
43/103 43
Recursivite mutuelle (1/2)
Recursivite mutuelle : une fonction f peut faire reference a unefonction g qui elle-meme fait reference a f
Dans un fichier pair.ml :
let rec pair n = (n = 0) || impair (n-1) ;;
let rec impair n = (n <> 0) && pair (n-1) ;;
compilation
> ocamlc -o pair pair.ml
File "pair.ml", line 1, characters 28-34:
Unbound value impair
43/103 43
Recursivite mutuelle (2/2)
Pour definir deux fonctions f et g mutuellement recursives onutilise la construction let rec f = e1 and g = e2
let rec pair n = (n = 0) || impair (n-1)
and impair n = (n <> 0) && pair (n-1)
;;
44/103 44
Representation de donnees structurees
I les types de base (int, float, etc.) sont insuffisants pourrepresenter des donnees structurees (dates, matrices etc.)
I des codages existent, mais ils ne sont pas naturels (et parfoisaussi tres inefficaces)
dans ce cours nous allons etudier trois structures de donnees (etleurs operations associees)
1. les produits (n-uplets)
2. les produits nommes (enregistrements)
3. les sommes (constructeurs)
45/103 45
Les Types Produits
46/103 46
Le produit cartesien
La structure de donnees la plus simple pour former des valeurscomplexes est la paire (ou produit cartesien)
# (1, 2) ;;
- : int * int = (1,2)
Les composantes des paires peuvent etre de types differents
# (’a’, 2.7) ;;
- : char * float = (’a’,2.7)
47/103 47
Le produit cartesien
La structure de donnees la plus simple pour former des valeurscomplexes est la paire (ou produit cartesien)
# (1, 2) ;;
- : int * int = (1,2)
Les composantes des paires peuvent etre de types differents
# (’a’, 2.7) ;;
- : char * float = (’a’,2.7)
47/103 47
Le produit cartesien
La structure de donnees la plus simple pour former des valeurscomplexes est la paire (ou produit cartesien)
# (1, 2) ;;
- : int * int = (1,2)
Les composantes des paires peuvent etre de types differents
# (’a’, 2.7) ;;
- : char * float = (’a’,2.7)
47/103 47
Le produit cartesien
La structure de donnees la plus simple pour former des valeurscomplexes est la paire (ou produit cartesien)
# (1, 2) ;;
- : int * int = (1,2)
Les composantes des paires peuvent etre de types differents
# (’a’, 2.7) ;;
- : char * float = (’a’,2.7)
47/103 47
Le produit cartesien
La structure de donnees la plus simple pour former des valeurscomplexes est la paire (ou produit cartesien)
# (1, 2) ;;
- : int * int = (1,2)
Les composantes des paires peuvent etre de types differents
# (’a’, 2.7) ;;
- : char * float = (’a’,2.7)
47/103 47
Le produit cartesien
La structure de donnees la plus simple pour former des valeurscomplexes est la paire (ou produit cartesien)
# (1, 2) ;;
- : int * int = (1,2)
Les composantes des paires peuvent etre de types differents
# (’a’, 2.7) ;;
- : char * float = (’a’,2.7)
47/103 47
Acceder aux composantes d’une paire
Les fonctions fst et snd permettent respectivement d’acceder a lapremiere et la deuxieme composante d’une paire
# snd (1,2);;
- : int = 2
# fst ((1,2.5),’a’);;
- : int * float = (1,2.5)
# let p = (1,2) in (snd p, fst p);;
- : int * int = (2,1)
48/103 48
Acceder aux composantes d’une paire
Les fonctions fst et snd permettent respectivement d’acceder a lapremiere et la deuxieme composante d’une paire
# snd (1,2);;
- : int = 2
# fst ((1,2.5),’a’);;
- : int * float = (1,2.5)
# let p = (1,2) in (snd p, fst p);;
- : int * int = (2,1)
48/103 48
Acceder aux composantes d’une paire
Les fonctions fst et snd permettent respectivement d’acceder a lapremiere et la deuxieme composante d’une paire
# snd (1,2);;
- : int = 2
# fst ((1,2.5),’a’);;
- : int * float = (1,2.5)
# let p = (1,2) in (snd p, fst p);;
- : int * int = (2,1)
48/103 48
Acceder aux composantes d’une paire
Les fonctions fst et snd permettent respectivement d’acceder a lapremiere et la deuxieme composante d’une paire
# snd (1,2);;
- : int = 2
# fst ((1,2.5),’a’);;
- : int * float = (1,2.5)
# let p = (1,2) in (snd p, fst p);;
- : int * int = (2,1)
48/103 48
Acceder aux composantes d’une paire
Les fonctions fst et snd permettent respectivement d’acceder a lapremiere et la deuxieme composante d’une paire
# snd (1,2);;
- : int = 2
# fst ((1,2.5),’a’);;
- : int * float = (1,2.5)
# let p = (1,2) in (snd p, fst p);;
- : int * int = (2,1)
48/103 48
Acceder aux composantes d’une paire
Les fonctions fst et snd permettent respectivement d’acceder a lapremiere et la deuxieme composante d’une paire
# snd (1,2);;
- : int = 2
# fst ((1,2.5),’a’);;
- : int * float = (1,2.5)
# let p = (1,2) in (snd p, fst p);;
- : int * int = (2,1)
48/103 48
Acceder aux composantes d’une paire
Les fonctions fst et snd permettent respectivement d’acceder a lapremiere et la deuxieme composante d’une paire
# snd (1,2);;
- : int = 2
# fst ((1,2.5),’a’);;
- : int * float = (1,2.5)
# let p = (1,2) in (snd p, fst p);;
- : int * int = (2,1)
48/103 48
n-uplets
Le produit cartesien (binaire) se generalise facilement afin deregrouper les valeurs en n-uplets
# (2+3,false||true,"bonjour");;
- : int * bool * string = (5,true,"bonjour")
Les n-uplets et les paires peuvent se melanger
# (’a’,1.2,(true,0));;
- : char * float * (bool * int) = (’a’,1.2,(true,0))
49/103 49
n-uplets
Le produit cartesien (binaire) se generalise facilement afin deregrouper les valeurs en n-uplets
# (2+3,false||true,"bonjour");;
- : int * bool * string = (5,true,"bonjour")
Les n-uplets et les paires peuvent se melanger
# (’a’,1.2,(true,0));;
- : char * float * (bool * int) = (’a’,1.2,(true,0))
49/103 49
n-uplets
Le produit cartesien (binaire) se generalise facilement afin deregrouper les valeurs en n-uplets
# (2+3,false||true,"bonjour");;
- : int * bool * string = (5,true,"bonjour")
Les n-uplets et les paires peuvent se melanger
# (’a’,1.2,(true,0));;
- : char * float * (bool * int) = (’a’,1.2,(true,0))
49/103 49
n-uplets
Le produit cartesien (binaire) se generalise facilement afin deregrouper les valeurs en n-uplets
# (2+3,false||true,"bonjour");;
- : int * bool * string = (5,true,"bonjour")
Les n-uplets et les paires peuvent se melanger
# (’a’,1.2,(true,0));;
- : char * float * (bool * int) = (’a’,1.2,(true,0))
49/103 49
n-uplets
Le produit cartesien (binaire) se generalise facilement afin deregrouper les valeurs en n-uplets
# (2+3,false||true,"bonjour");;
- : int * bool * string = (5,true,"bonjour")
Les n-uplets et les paires peuvent se melanger
# (’a’,1.2,(true,0));;
- : char * float * (bool * int) = (’a’,1.2,(true,0))
49/103 49
n-uplets
Le produit cartesien (binaire) se generalise facilement afin deregrouper les valeurs en n-uplets
# (2+3,false||true,"bonjour");;
- : int * bool * string = (5,true,"bonjour")
Les n-uplets et les paires peuvent se melanger
# (’a’,1.2,(true,0));;
- : char * float * (bool * int) = (’a’,1.2,(true,0))
49/103 49
Ne pas confondre...
. . . les types suivants
int * int * int (int * int) * int int * (int * int)
I le premier designe un triplet d’entiersI le second est une paire dont la premiere composante est une
paire d’entiers et la deuxieme un entierI le troisieme est une paire dont la premiere composante est un
entier et la deuxieme composante est une paire d’entiers
50/103 50
Ne pas confondre...
. . . les types suivants
int * int * int (int * int) * int int * (int * int)
I le premier designe un triplet d’entiers
I le second est une paire dont la premiere composante est unepaire d’entiers et la deuxieme un entier
I le troisieme est une paire dont la premiere composante est unentier et la deuxieme composante est une paire d’entiers
50/103 50
Ne pas confondre...
. . . les types suivants
int * int * int (int * int) * int int * (int * int)
I le premier designe un triplet d’entiersI le second est une paire dont la premiere composante est une
paire d’entiers et la deuxieme un entier
I le troisieme est une paire dont la premiere composante est unentier et la deuxieme composante est une paire d’entiers
50/103 50
Ne pas confondre...
. . . les types suivants
int * int * int (int * int) * int int * (int * int)
I le premier designe un triplet d’entiersI le second est une paire dont la premiere composante est une
paire d’entiers et la deuxieme un entierI le troisieme est une paire dont la premiere composante est un
entier et la deuxieme composante est une paire d’entiers
50/103 50
Acces aux elements d’un n-uplet (1/2)
On utilise une forme generalisee du let
let motif = e
ou une construction match-with
match e with
motif -> ...
ou le motif permet de filtrer le n-uplet represente par l’expression e
# let v = (’a’,1.2,"bonjour");;
val v : char * float * string = (’a’,1.2,"bonjour")
On recupere les elements de v avec (x,y,z) comme motif
# let (x,y,z) = v;;
val x : char = ’a’
val y : float = 1.2
val z : string = "bonjour"
51/103 51
Acces aux elements d’un n-uplet (1/2)
On utilise une forme generalisee du let
let motif = e
ou une construction match-with
match e with
motif -> ...
ou le motif permet de filtrer le n-uplet represente par l’expression e
# let v = (’a’,1.2,"bonjour");;
val v : char * float * string = (’a’,1.2,"bonjour")
On recupere les elements de v avec (x,y,z) comme motif
# let (x,y,z) = v;;
val x : char = ’a’
val y : float = 1.2
val z : string = "bonjour"
51/103 51
Acces aux elements d’un n-uplet (1/2)
On utilise une forme generalisee du let
let motif = e
ou une construction match-with
match e with
motif -> ...
ou le motif permet de filtrer le n-uplet represente par l’expression e
# let v = (’a’,1.2,"bonjour");;
val v : char * float * string = (’a’,1.2,"bonjour")
On recupere les elements de v avec (x,y,z) comme motif
# let (x,y,z) = v;;
val x : char = ’a’
val y : float = 1.2
val z : string = "bonjour"51/103 51
Acces aux elements d’un n-uplet (2/2)
# let v = (1,(’a’,2.3));;
val v : int * (char * float) = (1,(’a’,2.3))
Acces aux composantes d’une paire
# let (x,c) = v;;
val x : int = 1
val c : char * float = (’a’,2.3)
Acces aux composantes d’une composante
# let (x,(y,z)) = v;;
val x : int = 1
val y : char = ’a’
val z : float = 2.3
52/103 52
Acces aux elements d’un n-uplet (2/2)
# let v = (1,(’a’,2.3));;
val v : int * (char * float) = (1,(’a’,2.3))
Acces aux composantes d’une paire
# let (x,c) = v;;
val x : int = 1
val c : char * float = (’a’,2.3)
Acces aux composantes d’une composante
# let (x,(y,z)) = v;;
val x : int = 1
val y : char = ’a’
val z : float = 2.3
52/103 52
Acces aux elements d’un n-uplet (2/2)
# let v = (1,(’a’,2.3));;
val v : int * (char * float) = (1,(’a’,2.3))
Acces aux composantes d’une paire
# let (x,c) = v;;
val x : int = 1
val c : char * float = (’a’,2.3)
Acces aux composantes d’une composante
# let (x,(y,z)) = v;;
val x : int = 1
val y : char = ’a’
val z : float = 2.3
52/103 52
Acces aux elements d’un n-uplet (2/2)
# let v = (1,(’a’,2.3));;
val v : int * (char * float) = (1,(’a’,2.3))
Acces aux composantes d’une paire
# let (x,c) = v;;
val x : int = 1
val c : char * float = (’a’,2.3)
Acces aux composantes d’une composante
# let (x,(y,z)) = v;;
val x : int = 1
val y : char = ’a’
val z : float = 2.3
52/103 52
Acces aux elements d’un n-uplet (2/2)
# let v = (1,(’a’,2.3));;
val v : int * (char * float) = (1,(’a’,2.3))
Acces aux composantes d’une paire
# let (x,c) = v;;
val x : int = 1
val c : char * float = (’a’,2.3)
Acces aux composantes d’une composante
# let (x,(y,z)) = v;;
val x : int = 1
val y : char = ’a’
val z : float = 2.3
52/103 52
N-uplets : valeurs de premiere classe
Les n-uplets peuvent etre passes en arguments aux fonctions
# let f (x,y,z) = x + y * (int_of_float z);;
val f : int * int * float -> int = <fun>
# f (1,2,3.5);;
- : int = 7
ou retournes comme resultats
# let rec division n m =
if n<m then (0,n)
else
let (q,r) = division (n - m) m in (q + 1,r);;
val division : int -> int -> int * int = <fun>
53/103 53
N-uplets : valeurs de premiere classe
Les n-uplets peuvent etre passes en arguments aux fonctions
# let f (x,y,z) = x + y * (int_of_float z);;
val f : int * int * float -> int = <fun>
# f (1,2,3.5);;
- : int = 7
ou retournes comme resultats
# let rec division n m =
if n<m then (0,n)
else
let (q,r) = division (n - m) m in (q + 1,r);;
val division : int -> int -> int * int = <fun>
53/103 53
N-uplets : valeurs de premiere classe
Les n-uplets peuvent etre passes en arguments aux fonctions
# let f (x,y,z) = x + y * (int_of_float z);;
val f : int * int * float -> int = <fun>
# f (1,2,3.5);;
- : int = 7
ou retournes comme resultats
# let rec division n m =
if n<m then (0,n)
else
let (q,r) = division (n - m) m in (q + 1,r);;
val division : int -> int -> int * int = <fun>
53/103 53
N-uplets : valeurs de premiere classe
Les n-uplets peuvent etre passes en arguments aux fonctions
# let f (x,y,z) = x + y * (int_of_float z);;
val f : int * int * float -> int = <fun>
# f (1,2,3.5);;
- : int = 7
ou retournes comme resultats
# let rec division n m =
if n<m then (0,n)
else
let (q,r) = division (n - m) m in (q + 1,r);;
val division : int -> int -> int * int = <fun>53/103 53
Exemple
Calcul efficace de la fonction de Fibonacci dans fibonacci.ml
let fibonacci n =
let rec fib_rapide n =
if n=0 then (0,1)
else let (x,y) = fib_rapide (n-1) in (y,x+y)
in
fst (fib_rapide n)
print_int (fibonacci 15);;
compilation
> ocamlc -o fibonacci fibonacci.ml
execution
> ./fibonacci
610
54/103 54
Exemple
Calcul efficace de la fonction de Fibonacci dans fibonacci.ml
let fibonacci n =
let rec fib_rapide n =
if n=0 then (0,1)
else let (x,y) = fib_rapide (n-1) in (y,x+y)
in
fst (fib_rapide n)
print_int (fibonacci 15);;
compilation
> ocamlc -o fibonacci fibonacci.ml
execution
> ./fibonacci
610
54/103 54
Exemple
Calcul efficace de la fonction de Fibonacci dans fibonacci.ml
let fibonacci n =
let rec fib_rapide n =
if n=0 then (0,1)
else let (x,y) = fib_rapide (n-1) in (y,x+y)
in
fst (fib_rapide n)
print_int (fibonacci 15);;
compilation
> ocamlc -o fibonacci fibonacci.ml
execution
> ./fibonacci
61054/103 54
Inconvenients des n-uplets (1/2)
I Les objets representes par des n-uplets ne sont pas identifiesde maniere unique
I Le systeme de types du langage ne peut donc pas etre utilisepour garantir de ”bonnes” proprietes
Exemple : on represente les nombres complexes par des paires(r,i) de type float*float ou r et i sont respectivement lapartie reelle et la partie imaginaire du complexe
I Malheureusement ce type peut tout aussi bien representerdes nombres complexes en notation polaire, ou desintervalles etc.
I Comment alors garantir que la fonction suivante est bienutilisee pour ajouter des complexes ?
# let add (r1,i1) (r2,i2) = (r1+.r2 , i1+.i2);;
val add: float*float -> float*float -> float*float = <fun>
55/103 55
Inconvenients des n-uplets (2/2)
Les n-uplets avec un grand nombre de composantes deviennenttres vite inutilisables en pratique
Exemple : une fiche d’un fichier de personnes (nom, prenom,adresse, date de naissance, telephone fixe, telephone portable,etc.)
# let v =
("Durand","Jacques",
("2 rue J.Monod", "Orsay Cedex", 91893),
(10,03,1967), "0130452637","0645362738" ...)
I La consultation des informations devient vite penibleI Plusieurs elements du n-uplet peuvent avoir le meme type
(ex. nom et prenom) et il est facile de les confondre (sans quele systeme de type puisse nous aider)
56/103 56
Produits Nommes
57/103 57
les enregistrements
Le produit nomme permet de definir des enregistrements : desn-uplets dont les elements (champs) ont chacun un nom distinct
En OCAML, chaque produit nomme (ou type enregistrement)possede un nom donne par l’utilisateur
# type complexe = { re : float; im : float};;
type complexe = { re : float; im : float}
On cree des valeurs de type complexe de la maniere suivante
# { re = 1.4; im = 0.5};;
- : complexe = { re = 1.4; im = 0.5}
58/103 58
les enregistrements
Le produit nomme permet de definir des enregistrements : desn-uplets dont les elements (champs) ont chacun un nom distinct
En OCAML, chaque produit nomme (ou type enregistrement)possede un nom donne par l’utilisateur
# type complexe = { re : float; im : float};;
type complexe = { re : float; im : float}
On cree des valeurs de type complexe de la maniere suivante
# { re = 1.4; im = 0.5};;
- : complexe = { re = 1.4; im = 0.5}
58/103 58
les enregistrements
Le produit nomme permet de definir des enregistrements : desn-uplets dont les elements (champs) ont chacun un nom distinct
En OCAML, chaque produit nomme (ou type enregistrement)possede un nom donne par l’utilisateur
# type complexe = { re : float; im : float};;
type complexe = { re : float; im : float}
On cree des valeurs de type complexe de la maniere suivante
# { re = 1.4; im = 0.5};;
- : complexe = { re = 1.4; im = 0.5}
58/103 58
les enregistrements
Le produit nomme permet de definir des enregistrements : desn-uplets dont les elements (champs) ont chacun un nom distinct
En OCAML, chaque produit nomme (ou type enregistrement)possede un nom donne par l’utilisateur
# type complexe = { re : float; im : float};;
type complexe = { re : float; im : float}
On cree des valeurs de type complexe de la maniere suivante
# { re = 1.4; im = 0.5};;
- : complexe = { re = 1.4; im = 0.5}
58/103 58
Acces aux elements d’un enregistrement (1/2)
L’acces le plus simple aux champs d’un enregistrement se fait al’aide de la notation
objet.nom du champ
# let v = { re = 1.3; im = 0.9};;
val v : complexe = { re = 1.3; im = 0.9}
# v.re;;
- : float = 1.3
59/103 59
Acces aux elements d’un enregistrement (1/2)
L’acces le plus simple aux champs d’un enregistrement se fait al’aide de la notation
objet.nom du champ
# let v = { re = 1.3; im = 0.9};;
val v : complexe = { re = 1.3; im = 0.9}
# v.re;;
- : float = 1.3
59/103 59
Acces aux elements d’un enregistrement (1/2)
L’acces le plus simple aux champs d’un enregistrement se fait al’aide de la notation
objet.nom du champ
# let v = { re = 1.3; im = 0.9};;
val v : complexe = { re = 1.3; im = 0.9}
# v.re;;
- : float = 1.3
59/103 59
Acces aux elements d’un enregistrement (2/2)
Le filtrage permet un acces partiel et en profondeur aux champsd’un enregistrement
# type t = { a : int; b : float * char; c : string };;
type t = { a : int; b : float * char; c : string }
# let v = { a = 1; b = (3.4,’a’); c = "bonjour" };;
val v : t = { a = 1; b = (3.4,’a’); c = "bonjour" }
# let { b = (_,x); c = y} = v;;
val x : char = ’a’
val y : string = "bonjour"
60/103 60
Acces aux elements d’un enregistrement (2/2)
Le filtrage permet un acces partiel et en profondeur aux champsd’un enregistrement
# type t = { a : int; b : float * char; c : string };;
type t = { a : int; b : float * char; c : string }
# let v = { a = 1; b = (3.4,’a’); c = "bonjour" };;
val v : t = { a = 1; b = (3.4,’a’); c = "bonjour" }
# let { b = (_,x); c = y} = v;;
val x : char = ’a’
val y : string = "bonjour"
60/103 60
Acces aux elements d’un enregistrement (2/2)
Le filtrage permet un acces partiel et en profondeur aux champsd’un enregistrement
# type t = { a : int; b : float * char; c : string };;
type t = { a : int; b : float * char; c : string }
# let v = { a = 1; b = (3.4,’a’); c = "bonjour" };;
val v : t = { a = 1; b = (3.4,’a’); c = "bonjour" }
# let { b = (_,x); c = y} = v;;
val x : char = ’a’
val y : string = "bonjour"
60/103 60
L’ordre des champs n’a pas d’importance
Les definitions de types suivantes sont equivalentes
type t = { a : int; b : char; c : bool }
type t = { b : char; c : bool; a : int }
De meme ces valeurs sont egales :
# { a = 1; b = ’t’; c = true} = { b = ’t’; c = true; a = 1} ;;
- : bool = true
Le filtrage est egalement insensible a l’ordre des champs :
# let { c = x; b = y} = { b = ’t’; c = true; a = 1} ;;
val x : bool = true
val y : char = ’t’
61/103 61
L’ordre des champs n’a pas d’importance
Les definitions de types suivantes sont equivalentes
type t = { a : int; b : char; c : bool }
type t = { b : char; c : bool; a : int }
De meme ces valeurs sont egales :
# { a = 1; b = ’t’; c = true} = { b = ’t’; c = true; a = 1} ;;
- : bool = true
Le filtrage est egalement insensible a l’ordre des champs :
# let { c = x; b = y} = { b = ’t’; c = true; a = 1} ;;
val x : bool = true
val y : char = ’t’
61/103 61
L’ordre des champs n’a pas d’importance
Les definitions de types suivantes sont equivalentes
type t = { a : int; b : char; c : bool }
type t = { b : char; c : bool; a : int }
De meme ces valeurs sont egales :
# { a = 1; b = ’t’; c = true} = { b = ’t’; c = true; a = 1} ;;
- : bool = true
Le filtrage est egalement insensible a l’ordre des champs :
# let { c = x; b = y} = { b = ’t’; c = true; a = 1} ;;
val x : bool = true
val y : char = ’t’
61/103 61
structurer l’information
Le melange des n-uplets et des enregistrements permet de definirdes objets complexes
# type adresse = { rue : string; ville : string; cp : int};;
# type fiche = {
nom : string;
prenom : string ;
adresse : adresse;
date_naissance : int * int * int;
tel_fixe : string;
portable : string
};;
62/103 62
Creation de nouvelles valeurs
# let v1 = { a = 1; b = false; c = ’r’};;
val v1 : t = { a = 1; b = false; c = ’r’}
On peut creer un nouvel enregistrement v2 en utilisant le contenudes champs de v1
# let v2 = { a = v1.a; b = true; c = v1.c};;
val v2 : t = { a = 1; b = true; c = ’r’}
Le raccourci syntaxique suivant permet d’arriver au meme resultat
{v with c1 = e1; ... cn=en}
# let v3 = { v1 with b = true };;
val v3 : t = { a = 1; b = true; c = ’r’}
63/103 63
Creation de nouvelles valeurs
# let v1 = { a = 1; b = false; c = ’r’};;
val v1 : t = { a = 1; b = false; c = ’r’}
On peut creer un nouvel enregistrement v2 en utilisant le contenudes champs de v1
# let v2 = { a = v1.a; b = true; c = v1.c};;
val v2 : t = { a = 1; b = true; c = ’r’}
Le raccourci syntaxique suivant permet d’arriver au meme resultat
{v with c1 = e1; ... cn=en}
# let v3 = { v1 with b = true };;
val v3 : t = { a = 1; b = true; c = ’r’}
63/103 63
Creation de nouvelles valeurs
# let v1 = { a = 1; b = false; c = ’r’};;
val v1 : t = { a = 1; b = false; c = ’r’}
On peut creer un nouvel enregistrement v2 en utilisant le contenudes champs de v1
# let v2 = { a = v1.a; b = true; c = v1.c};;
val v2 : t = { a = 1; b = true; c = ’r’}
Le raccourci syntaxique suivant permet d’arriver au meme resultat
{v with c1 = e1; ... cn=en}
# let v3 = { v1 with b = true };;
val v3 : t = { a = 1; b = true; c = ’r’}
63/103 63
Creation de nouvelles valeurs
# let v1 = { a = 1; b = false; c = ’r’};;
val v1 : t = { a = 1; b = false; c = ’r’}
On peut creer un nouvel enregistrement v2 en utilisant le contenudes champs de v1
# let v2 = { a = v1.a; b = true; c = v1.c};;
val v2 : t = { a = 1; b = true; c = ’r’}
Le raccourci syntaxique suivant permet d’arriver au meme resultat
{v with c1 = e1; ... cn=en}
# let v3 = { v1 with b = true };;
val v3 : t = { a = 1; b = true; c = ’r’}
63/103 63
Creation de nouvelles valeurs
# let v1 = { a = 1; b = false; c = ’r’};;
val v1 : t = { a = 1; b = false; c = ’r’}
On peut creer un nouvel enregistrement v2 en utilisant le contenudes champs de v1
# let v2 = { a = v1.a; b = true; c = v1.c};;
val v2 : t = { a = 1; b = true; c = ’r’}
Le raccourci syntaxique suivant permet d’arriver au meme resultat
{v with c1 = e1; ... cn=en}
# let v3 = { v1 with b = true };;
val v3 : t = { a = 1; b = true; c = ’r’}
63/103 63
Enregistrements : valeurs de premiere classe
Une fonction prenant un enregistrement en argument :
# let f v = v.a;;
val f : t -> int
# f {a = 1; b = false; c = ’e’};;
- : int = 1
Les enregistrements peuvent aussi etre retournes en resultat
# let f {a=x} v = { v with a = x+v.a } ;;
val f : t -> t -> t
# let v = {a=1;b=true;c=’r’} in f v v;;
- : t = {a=2;b=true;c=’r’}
64/103 64
Enregistrements : valeurs de premiere classe
Une fonction prenant un enregistrement en argument :
# let f v = v.a;;
val f : t -> int
# f {a = 1; b = false; c = ’e’};;
- : int = 1
Les enregistrements peuvent aussi etre retournes en resultat
# let f {a=x} v = { v with a = x+v.a } ;;
val f : t -> t -> t
# let v = {a=1;b=true;c=’r’} in f v v;;
- : t = {a=2;b=true;c=’r’}
64/103 64
Enregistrements : valeurs de premiere classe
Une fonction prenant un enregistrement en argument :
# let f v = v.a;;
val f : t -> int
# f {a = 1; b = false; c = ’e’};;
- : int = 1
Les enregistrements peuvent aussi etre retournes en resultat
# let f {a=x} v = { v with a = x+v.a } ;;
val f : t -> t -> t
# let v = {a=1;b=true;c=’r’} in f v v;;
- : t = {a=2;b=true;c=’r’}
64/103 64
Enregistrements : valeurs de premiere classe
Une fonction prenant un enregistrement en argument :
# let f v = v.a;;
val f : t -> int
# f {a = 1; b = false; c = ’e’};;
- : int = 1
Les enregistrements peuvent aussi etre retournes en resultat
# let f {a=x} v = { v with a = x+v.a } ;;
val f : t -> t -> t
# let v = {a=1;b=true;c=’r’} in f v v;;
- : t = {a=2;b=true;c=’r’}
64/103 64
Sommes
65/103 65
Les types sommes
I Modelisation de domaines finisI Realisation de sommes disjointes permettant de reunir dans
un meme type des valeurs pouvant appartenir a des typesdifferents
66/103 66
Constructeurs constants
On peut modeliser un domaine fini comportant exactement nvaleurs avec un type somme
Exemple, les couleurs d’un jeu de carte :
# type couleur = Pique | Coeur | Carreau | Trefle;;
type couleur = Pique | Coeur | Carreau | Trefle
I les identificateurs Pique, Coeur, Carreau et Trefle sont desconstructeurs (les majuscules sont obligatoires pour definirdes constructeurs)
I le nom du domaine fini est couleur
67/103 67
Utilisation des constructeurs
L’unique maniere de creer des valeurs d’un type somme estd’utiliser un constructeur :
# Trefle;;
- : couleur = Trefle
# let v = (Pique , Coeur);;
val v : couleur * couleur = (Pique , Coeur)
# Pique = Coeur;;
- : bool = false
68/103 68
Utilisation des constructeurs
L’unique maniere de creer des valeurs d’un type somme estd’utiliser un constructeur :
# Trefle;;
- : couleur = Trefle
# let v = (Pique , Coeur);;
val v : couleur * couleur = (Pique , Coeur)
# Pique = Coeur;;
- : bool = false
68/103 68
Utilisation des constructeurs
L’unique maniere de creer des valeurs d’un type somme estd’utiliser un constructeur :
# Trefle;;
- : couleur = Trefle
# let v = (Pique , Coeur);;
val v : couleur * couleur = (Pique , Coeur)
# Pique = Coeur;;
- : bool = false
68/103 68
Utilisation des constructeurs
L’unique maniere de creer des valeurs d’un type somme estd’utiliser un constructeur :
# Trefle;;
- : couleur = Trefle
# let v = (Pique , Coeur);;
val v : couleur * couleur = (Pique , Coeur)
# Pique = Coeur;;
- : bool = false
68/103 68
Filtrage des constructeurs constants
La construction match-with permet de definir de maniere compacteune analyse par cas d’un type somme
# let points v =
match v with
Pique -> 1
| Trefle -> 2
| Coeur -> 3
| Carreau -> 4;;
val points : couleur -> int = <fun>
# points Coeur;;
- : int = 3
69/103 69
Filtrage des constructeurs constants
La construction match-with permet de definir de maniere compacteune analyse par cas d’un type somme
# let points v =
match v with
Pique -> 1
| Trefle -> 2
| Coeur -> 3
| Carreau -> 4;;
val points : couleur -> int = <fun>
# points Coeur;;
- : int = 3
69/103 69
Filtrage des constructeurs constants
La construction match-with permet de definir de maniere compacteune analyse par cas d’un type somme
# let points v =
match v with
Pique -> 1
| Trefle -> 2
| Coeur -> 3
| Carreau -> 4;;
val points : couleur -> int = <fun>
# points Coeur;;
- : int = 3
69/103 69
Filtrage des constructeurs constants
La construction match-with permet de definir de maniere compacteune analyse par cas d’un type somme
# let points v =
match v with
Pique -> 1
| Trefle -> 2
| Coeur -> 3
| Carreau -> 4;;
val points : couleur -> int = <fun>
# points Coeur;;
- : int = 3
69/103 69
Constructeurs avec arguments
Les constructeurs peuvent egalement avoir des arguments, parexemple :
# type num = Int of int | Float of float
type num = Int of int | Float of float
Le mot-cle of indique que le constructeur attend un argument
On cree des valeurs en appliquant les constructeurs a desarguments du bon type :
# Int(5);;
- : num = Int(5)
70/103 70
Constructeurs avec arguments
Les constructeurs peuvent egalement avoir des arguments, parexemple :
# type num = Int of int | Float of float
type num = Int of int | Float of float
Le mot-cle of indique que le constructeur attend un argument
On cree des valeurs en appliquant les constructeurs a desarguments du bon type :
# Int(5);;
- : num = Int(5)
70/103 70
Filtrage des constructeurs avec arguments (1/2)
On utilise la construction match-with pour recuperer les argumentsassocies a un constructeur
# match Int(5) with Int(x) -> x+2;;
- : int = 7
En realite, la reponse que l’on obtient est celle-la :
Warning P: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
Float _
- : int = 7
Le filtrage exige de faire une analyse par cas complete en fonctiondu type de l’objet filtre et non de sa valeur.
71/103 71
Filtrage des constructeurs avec arguments (1/2)
On utilise la construction match-with pour recuperer les argumentsassocies a un constructeur
# match Int(5) with Int(x) -> x+2;;
- : int = 7
En realite, la reponse que l’on obtient est celle-la :
Warning P: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
Float _
- : int = 7
Le filtrage exige de faire une analyse par cas complete en fonctiondu type de l’objet filtre et non de sa valeur.
71/103 71
Filtrage des constructeurs avec arguments (1/2)
On utilise la construction match-with pour recuperer les argumentsassocies a un constructeur
# match Int(5) with Int(x) -> x+2;;
- : int = 7
En realite, la reponse que l’on obtient est celle-la :
Warning P: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
Float _
- : int = 7
Le filtrage exige de faire une analyse par cas complete en fonctiondu type de l’objet filtre et non de sa valeur.
71/103 71
Filtrage des constructeurs avec arguments (2/2)
La completude de l’analyse peut etre obtenue en executantfailwith "explication" pour les cas impossibles
# match Int(5) with
Int(x) -> x+2
| Float(x) -> failwith "cas impossible";;
- : int = 7
72/103 72
Filtrage des constructeurs avec arguments (2/2)
La completude de l’analyse peut etre obtenue en executantfailwith "explication" pour les cas impossibles
# match Int(5) with
Int(x) -> x+2
| Float(x) -> failwith "cas impossible";;
- : int = 7
72/103 72
Exemple
L’addition de valeurs de type num peut s’ecrire ainsi
# let ajoute x y =
match (x,y) with
(Int(m) , Int(n)) -> Int(m + n)
| (Int(m) , Float(n)) -> Float((float_of_int m) +. n)
| (Float(m) , Int(n)) -> Float(m +. (float_of_int n))
| (Float(m) , Float(n)) -> Float(m +. n);;
val ajoute : num -> num -> num = <fun>
On utilise simplement cette fonction de la maniere suivante
# ajoute (Float(3.5)) (Int(5));;
- : num = Float(8.5)
73/103 73
Exemple
L’addition de valeurs de type num peut s’ecrire ainsi
# let ajoute x y =
match (x,y) with
(Int(m) , Int(n)) -> Int(m + n)
| (Int(m) , Float(n)) -> Float((float_of_int m) +. n)
| (Float(m) , Int(n)) -> Float(m +. (float_of_int n))
| (Float(m) , Float(n)) -> Float(m +. n);;
val ajoute : num -> num -> num = <fun>
On utilise simplement cette fonction de la maniere suivante
# ajoute (Float(3.5)) (Int(5));;
- : num = Float(8.5)
73/103 73
Exemple
L’addition de valeurs de type num peut s’ecrire ainsi
# let ajoute x y =
match (x,y) with
(Int(m) , Int(n)) -> Int(m + n)
| (Int(m) , Float(n)) -> Float((float_of_int m) +. n)
| (Float(m) , Int(n)) -> Float(m +. (float_of_int n))
| (Float(m) , Float(n)) -> Float(m +. n);;
val ajoute : num -> num -> num = <fun>
On utilise simplement cette fonction de la maniere suivante
# ajoute (Float(3.5)) (Int(5));;
- : num = Float(8.5)
73/103 73
Exemple
L’addition de valeurs de type num peut s’ecrire ainsi
# let ajoute x y =
match (x,y) with
(Int(m) , Int(n)) -> Int(m + n)
| (Int(m) , Float(n)) -> Float((float_of_int m) +. n)
| (Float(m) , Int(n)) -> Float(m +. (float_of_int n))
| (Float(m) , Float(n)) -> Float(m +. n);;
val ajoute : num -> num -> num = <fun>
On utilise simplement cette fonction de la maniere suivante
# ajoute (Float(3.5)) (Int(5));;
- : num = Float(8.5)
73/103 73
Autre exemple
Le type des cartes d’un jeu de cartes :
type valeur = Roi | Reine | Valet | Num of int
type couleur = Coeur | Pique | Trefle | Carreau
type carte = valeur * couleur
# let compare (c1,_) (c2,_) =
if c1 = c2 then 0
else match c1,c2 with
| Roi, _ -> 1
| Reine, Roi -> -1
| Reine, _ -> 1
| Valet, Roi -> -1
| Valet, Reine -> -1
| Valet, _ -> 1
| Num(x), Num(y) -> (x - y) / (abs (x - y))
| _ -> -1
74/103 74
Le type des sequences (listes)
75/103 75
Sequences d’entiers
On peut utiliser un type somme pour representer des sequences(non bornees) d’entiers :
# type int_list = Nil | Cons of int * int_list;;
I Nil represente la sequence videI Cons(x,l) est la sequence dont le premier element (on dit
aussi la tete) est x et la suite est la sequence l
Par exemple, la sequence 4;1;5;8;1 est representee par lavaleur :
# Cons(4,Cons(1,Cons(5,Cons(8,Cons(1,Nil)))));;
- : int_list = Cons(4,Cons(1,Cons(5,Cons(8,Cons(1,Nil)))))
76/103 76
Le type int list
Le type des sequences est predefini en OCAML et ses elementsse notent avec une syntaxe speciale
I Cons se note :: et est infixeI Nil se note []
Par exemple, la sequence 4;1;5;8;1 est representee par :
# 4::1::5::8::1::[];;
- : int list = [4;1;5;8;1]
On peut aussi directement utiliser la notation [e1;e2;...;en]
# [4;1;5;8;1];;
- : int list = [4;1;5;8;1]
ou faire un melange des deux notations :
# 4::1::[5;8;1];;
- : int list = [4;1;5;8;1]77/103 77
Des listes de types quelconques
OCAML permet de definir des listes dont les elements peuvent etreautre chose que des entiers :
# [’c’;’a’;’m’;’l’];;
- : char list = [’c’;’a’;’m’;’l’]
# [["des";"listes"];["de";"listes"]];;
- : string list list = [["des";"listes"];["de";"listes"]]
Mais il n’est pas possible de construire une liste d’elements detypes differents :
# [10;’a’;4];;
---
This expression has type char but is here used
with type int
78/103 78
Listes chaınees
Les listes predefinies en OCAML correspondent exactement auxlistes chaınees definies habituellement en C par le type suivant
typedef struct list{
int elt;
struct list* suivant;
};
La representation memoire de ces listes correspond a un chaınagede blocs memoire, par exemple, la liste [1; 2; 3] correspond a :
1 2
30 ([])
79/103 79
Acces aux elements d’une liste
On accede aux elements d’une liste a l’aide des fonctionspredefinies List.hd et List.tl
# List.hd [3;6;1;2];;
- : int = 3
# List.tl [3;6;1;2];;
- : int list = [6;1;2];;
List.hd et List.tl echouent sur une liste vide
# List.hd [];;
Exception: Failure "hd".
# List.tail [];;
Exception: Failure "tl".
80/103 80
Fonctions sur les listes
81/103 81
Definitions de fonctions sur les listes
La definition des fonctions sur les listes prennent generalement laforme d’une definition a deux cas :
I le cas ou liste est videI le cas ou elle ne l’est pas
Pour cette raison, il est plus agreable de realiser cette analyse parcas avec du filtrage :
# let f l =
match l with
[] -> ...
| x::s -> ...
82/103 82
la fonction zeros
La fonction zeros verifie que tous les elements d’une listed’entiers sont des 0 (renvoie true si la liste est vide)
# let rec zeros l =
match l with
[] -> true
| x::s -> x=0 && zeros s ;;
val zeros : int list -> bool = <fun>
# zeros [];;
- : bool = true
# zeros [0;0;0];;
- : bool = true
# zeros [0;1;0];;
- : bool = false
83/103 83
la fonction zeros
La fonction zeros verifie que tous les elements d’une listed’entiers sont des 0 (renvoie true si la liste est vide)
# let rec zeros l =
match l with
[] -> true
| x::s -> x=0 && zeros s ;;
val zeros : int list -> bool = <fun>
# zeros [];;
- : bool = true
# zeros [0;0;0];;
- : bool = true
# zeros [0;1;0];;
- : bool = false
83/103 83
la fonction zeros
La fonction zeros verifie que tous les elements d’une listed’entiers sont des 0 (renvoie true si la liste est vide)
# let rec zeros l =
match l with
[] -> true
| x::s -> x=0 && zeros s ;;
val zeros : int list -> bool = <fun>
# zeros [];;
- : bool = true
# zeros [0;0;0];;
- : bool = true
# zeros [0;1;0];;
- : bool = false
83/103 83
la fonction zeros
La fonction zeros verifie que tous les elements d’une listed’entiers sont des 0 (renvoie true si la liste est vide)
# let rec zeros l =
match l with
[] -> true
| x::s -> x=0 && zeros s ;;
val zeros : int list -> bool = <fun>
# zeros [];;
- : bool = true
# zeros [0;0;0];;
- : bool = true
# zeros [0;1;0];;
- : bool = false
83/103 83
Evaluation de la fonction zeros
Evaluation de zeros [0;0;0]
zeros [0;0;0]
[0; 0; 0] 6= [], x = 0, s = [0; 0] ⇒ 0=0 && zeros [0;0]
= zeros [0;0]
[0; 0] 6= [], x = 0, s = [0] ⇒ 0=0 && zeros [0]
= zeros [0]
[0] 6= [], x = 0, s = [] ⇒ 0=0 && zeros []
= zeros []
[] = [] ⇒ true
Evaluation de zeros [0;1;0]
zeros [0;1;0]
[0; 1; 0] 6= [], x = 0, s = [1; 0] ⇒ 0=0 && zeros [1;0]
= zeros [1;0]
[1; 0] 6= [], x = 1, s = [0] ⇒ 1=0 && zeros [0]
= false
84/103 84
Evaluation de la fonction zeros
Evaluation de zeros [0;0;0]
zeros [0;0;0]
[0; 0; 0] 6= [], x = 0, s = [0; 0] ⇒ 0=0 && zeros [0;0]
= zeros [0;0]
[0; 0] 6= [], x = 0, s = [0] ⇒ 0=0 && zeros [0]
= zeros [0]
[0] 6= [], x = 0, s = [] ⇒ 0=0 && zeros []
= zeros []
[] = [] ⇒ true
Evaluation de zeros [0;1;0]
zeros [0;1;0]
[0; 1; 0] 6= [], x = 0, s = [1; 0] ⇒ 0=0 && zeros [1;0]
= zeros [1;0]
[1; 0] 6= [], x = 1, s = [0] ⇒ 1=0 && zeros [0]
= false
84/103 84
Evaluation de la fonction zeros
Evaluation de zeros [0;0;0]
zeros [0;0;0]
[0; 0; 0] 6= [], x = 0, s = [0; 0] ⇒ 0=0 && zeros [0;0]
= zeros [0;0]
[0; 0] 6= [], x = 0, s = [0] ⇒ 0=0 && zeros [0]
= zeros [0]
[0] 6= [], x = 0, s = [] ⇒ 0=0 && zeros []
= zeros []
[] = [] ⇒ true
Evaluation de zeros [0;1;0]
zeros [0;1;0]
[0; 1; 0] 6= [], x = 0, s = [1; 0] ⇒ 0=0 && zeros [1;0]
= zeros [1;0]
[1; 0] 6= [], x = 1, s = [0] ⇒ 1=0 && zeros [0]
= false
84/103 84
Evaluation de la fonction zeros
Evaluation de zeros [0;0;0]
zeros [0;0;0]
[0; 0; 0] 6= [], x = 0, s = [0; 0] ⇒ 0=0 && zeros [0;0]
= zeros [0;0]
[0; 0] 6= [], x = 0, s = [0] ⇒ 0=0 && zeros [0]
= zeros [0]
[0] 6= [], x = 0, s = [] ⇒ 0=0 && zeros []
= zeros []
[] = [] ⇒ true
Evaluation de zeros [0;1;0]
zeros [0;1;0]
[0; 1; 0] 6= [], x = 0, s = [1; 0] ⇒ 0=0 && zeros [1;0]
= zeros [1;0]
[1; 0] 6= [], x = 1, s = [0] ⇒ 1=0 && zeros [0]
= false
84/103 84
Evaluation de la fonction zeros
Evaluation de zeros [0;0;0]
zeros [0;0;0]
[0; 0; 0] 6= [], x = 0, s = [0; 0] ⇒ 0=0 && zeros [0;0]
= zeros [0;0]
[0; 0] 6= [], x = 0, s = [0] ⇒ 0=0 && zeros [0]
= zeros [0]
[0] 6= [], x = 0, s = [] ⇒ 0=0 && zeros []
= zeros []
[] = [] ⇒ true
Evaluation de zeros [0;1;0]
zeros [0;1;0]
[0; 1; 0] 6= [], x = 0, s = [1; 0] ⇒ 0=0 && zeros [1;0]
= zeros [1;0]
[1; 0] 6= [], x = 1, s = [0] ⇒ 1=0 && zeros [0]
= false
84/103 84
Evaluation de la fonction zeros
Evaluation de zeros [0;0;0]
zeros [0;0;0]
[0; 0; 0] 6= [], x = 0, s = [0; 0] ⇒ 0=0 && zeros [0;0]
= zeros [0;0]
[0; 0] 6= [], x = 0, s = [0] ⇒ 0=0 && zeros [0]
= zeros [0]
[0] 6= [], x = 0, s = [] ⇒ 0=0 && zeros []
= zeros []
[] = [] ⇒ true
Evaluation de zeros [0;1;0]
zeros [0;1;0]
[0; 1; 0] 6= [], x = 0, s = [1; 0] ⇒ 0=0 && zeros [1;0]
= zeros [1;0]
[1; 0] 6= [], x = 1, s = [0] ⇒ 1=0 && zeros [0]
= false
84/103 84
Evaluation de la fonction zeros
Evaluation de zeros [0;0;0]
zeros [0;0;0]
[0; 0; 0] 6= [], x = 0, s = [0; 0] ⇒ 0=0 && zeros [0;0]
= zeros [0;0]
[0; 0] 6= [], x = 0, s = [0] ⇒ 0=0 && zeros [0]
= zeros [0]
[0] 6= [], x = 0, s = [] ⇒ 0=0 && zeros []
= zeros []
[] = [] ⇒ true
Evaluation de zeros [0;1;0]
zeros [0;1;0]
[0; 1; 0] 6= [], x = 0, s = [1; 0] ⇒ 0=0 && zeros [1;0]
= zeros [1;0]
[1; 0] 6= [], x = 1, s = [0] ⇒ 1=0 && zeros [0]
= false
84/103 84
Evaluation de la fonction zeros
Evaluation de zeros [0;0;0]
zeros [0;0;0]
[0; 0; 0] 6= [], x = 0, s = [0; 0] ⇒ 0=0 && zeros [0;0]
= zeros [0;0]
[0; 0] 6= [], x = 0, s = [0] ⇒ 0=0 && zeros [0]
= zeros [0]
[0] 6= [], x = 0, s = [] ⇒ 0=0 && zeros []
= zeros []
[] = [] ⇒ true
Evaluation de zeros [0;1;0]
zeros [0;1;0]
[0; 1; 0] 6= [], x = 0, s = [1; 0] ⇒ 0=0 && zeros [1;0]
= zeros [1;0]
[1; 0] 6= [], x = 1, s = [0] ⇒ 1=0 && zeros [0]
= false
84/103 84
Recherche d’un entier dans une liste
La fonction recherche determine si un entier n figure bien dansune liste l
# let rec recherche n l =
match l with
[] -> false
| x::s -> x=n || recherche n s
val recherche : int list -> bool = <fun>
# recherche 4 [3;2;4;1];;
- : bool = true
# recherche 4 [1;2];;
- : bool = false
85/103 85
Recherche d’un entier dans une liste
La fonction recherche determine si un entier n figure bien dansune liste l
# let rec recherche n l =
match l with
[] -> false
| x::s -> x=n || recherche n s
val recherche : int list -> bool = <fun>
# recherche 4 [3;2;4;1];;
- : bool = true
# recherche 4 [1;2];;
- : bool = false
85/103 85
Recherche d’un entier dans une liste
La fonction recherche determine si un entier n figure bien dansune liste l
# let rec recherche n l =
match l with
[] -> false
| x::s -> x=n || recherche n s
val recherche : int list -> bool = <fun>
# recherche 4 [3;2;4;1];;
- : bool = true
# recherche 4 [1;2];;
- : bool = false
85/103 85
Evaluation de la fonction recherche
Evaluation de recherche 4 [3;2;4;1]
recherche 4 [3;2;4;1]
[3; 2; 4; 1] 6= [], x = 3, s = [2; 4; 1] ⇒ 3=4 || recherche 4 [2;4;1]
= recherche 4 [2;4;1]
[2; 4; 1] 6= [], x = 2, s = [4; 1] ⇒ 2=4 || recherche [4;1]
= recherche 4 [4;1]
[4; 1] 6= [], x = 4, s = [1] ⇒ 4=4 || recherche 4 [1]
= true
Evaluation de recherche 4 [1;2]
recherche 4 [1;2]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1=4 || recherche 4 [2]
= recherche 4 [2]
[2] 6= [], x = 2, s = [] ⇒ 2=4 || recherche 4 []
= recherche 4 []
[] = [] ⇒ false
86/103 86
Evaluation de la fonction recherche
Evaluation de recherche 4 [3;2;4;1]
recherche 4 [3;2;4;1]
[3; 2; 4; 1] 6= [], x = 3, s = [2; 4; 1] ⇒ 3=4 || recherche 4 [2;4;1]
= recherche 4 [2;4;1]
[2; 4; 1] 6= [], x = 2, s = [4; 1] ⇒ 2=4 || recherche [4;1]
= recherche 4 [4;1]
[4; 1] 6= [], x = 4, s = [1] ⇒ 4=4 || recherche 4 [1]
= true
Evaluation de recherche 4 [1;2]
recherche 4 [1;2]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1=4 || recherche 4 [2]
= recherche 4 [2]
[2] 6= [], x = 2, s = [] ⇒ 2=4 || recherche 4 []
= recherche 4 []
[] = [] ⇒ false
86/103 86
Evaluation de la fonction recherche
Evaluation de recherche 4 [3;2;4;1]
recherche 4 [3;2;4;1]
[3; 2; 4; 1] 6= [], x = 3, s = [2; 4; 1] ⇒ 3=4 || recherche 4 [2;4;1]
= recherche 4 [2;4;1]
[2; 4; 1] 6= [], x = 2, s = [4; 1] ⇒ 2=4 || recherche [4;1]
= recherche 4 [4;1]
[4; 1] 6= [], x = 4, s = [1] ⇒ 4=4 || recherche 4 [1]
= true
Evaluation de recherche 4 [1;2]
recherche 4 [1;2]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1=4 || recherche 4 [2]
= recherche 4 [2]
[2] 6= [], x = 2, s = [] ⇒ 2=4 || recherche 4 []
= recherche 4 []
[] = [] ⇒ false
86/103 86
Evaluation de la fonction recherche
Evaluation de recherche 4 [3;2;4;1]
recherche 4 [3;2;4;1]
[3; 2; 4; 1] 6= [], x = 3, s = [2; 4; 1] ⇒ 3=4 || recherche 4 [2;4;1]
= recherche 4 [2;4;1]
[2; 4; 1] 6= [], x = 2, s = [4; 1] ⇒ 2=4 || recherche [4;1]
= recherche 4 [4;1]
[4; 1] 6= [], x = 4, s = [1] ⇒ 4=4 || recherche 4 [1]
= true
Evaluation de recherche 4 [1;2]
recherche 4 [1;2]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1=4 || recherche 4 [2]
= recherche 4 [2]
[2] 6= [], x = 2, s = [] ⇒ 2=4 || recherche 4 []
= recherche 4 []
[] = [] ⇒ false
86/103 86
Evaluation de la fonction recherche
Evaluation de recherche 4 [3;2;4;1]
recherche 4 [3;2;4;1]
[3; 2; 4; 1] 6= [], x = 3, s = [2; 4; 1] ⇒ 3=4 || recherche 4 [2;4;1]
= recherche 4 [2;4;1]
[2; 4; 1] 6= [], x = 2, s = [4; 1] ⇒ 2=4 || recherche [4;1]
= recherche 4 [4;1]
[4; 1] 6= [], x = 4, s = [1] ⇒ 4=4 || recherche 4 [1]
= true
Evaluation de recherche 4 [1;2]
recherche 4 [1;2]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1=4 || recherche 4 [2]
= recherche 4 [2]
[2] 6= [], x = 2, s = [] ⇒ 2=4 || recherche 4 []
= recherche 4 []
[] = [] ⇒ false
86/103 86
Evaluation de la fonction recherche
Evaluation de recherche 4 [3;2;4;1]
recherche 4 [3;2;4;1]
[3; 2; 4; 1] 6= [], x = 3, s = [2; 4; 1] ⇒ 3=4 || recherche 4 [2;4;1]
= recherche 4 [2;4;1]
[2; 4; 1] 6= [], x = 2, s = [4; 1] ⇒ 2=4 || recherche [4;1]
= recherche 4 [4;1]
[4; 1] 6= [], x = 4, s = [1] ⇒ 4=4 || recherche 4 [1]
= true
Evaluation de recherche 4 [1;2]
recherche 4 [1;2]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1=4 || recherche 4 [2]
= recherche 4 [2]
[2] 6= [], x = 2, s = [] ⇒ 2=4 || recherche 4 []
= recherche 4 []
[] = [] ⇒ false
86/103 86
Evaluation de la fonction recherche
Evaluation de recherche 4 [3;2;4;1]
recherche 4 [3;2;4;1]
[3; 2; 4; 1] 6= [], x = 3, s = [2; 4; 1] ⇒ 3=4 || recherche 4 [2;4;1]
= recherche 4 [2;4;1]
[2; 4; 1] 6= [], x = 2, s = [4; 1] ⇒ 2=4 || recherche [4;1]
= recherche 4 [4;1]
[4; 1] 6= [], x = 4, s = [1] ⇒ 4=4 || recherche 4 [1]
= true
Evaluation de recherche 4 [1;2]
recherche 4 [1;2]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1=4 || recherche 4 [2]
= recherche 4 [2]
[2] 6= [], x = 2, s = [] ⇒ 2=4 || recherche 4 []
= recherche 4 []
[] = [] ⇒ false
86/103 86
Evaluation de la fonction recherche
Evaluation de recherche 4 [3;2;4;1]
recherche 4 [3;2;4;1]
[3; 2; 4; 1] 6= [], x = 3, s = [2; 4; 1] ⇒ 3=4 || recherche 4 [2;4;1]
= recherche 4 [2;4;1]
[2; 4; 1] 6= [], x = 2, s = [4; 1] ⇒ 2=4 || recherche [4;1]
= recherche 4 [4;1]
[4; 1] 6= [], x = 4, s = [1] ⇒ 4=4 || recherche 4 [1]
= true
Evaluation de recherche 4 [1;2]
recherche 4 [1;2]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1=4 || recherche 4 [2]
= recherche 4 [2]
[2] 6= [], x = 2, s = [] ⇒ 2=4 || recherche 4 []
= recherche 4 []
[] = [] ⇒ false
86/103 86
Longueur d’une liste
La fonction longueur retourne la longueur d’une liste
# let rec longueur l =
match l with
[] -> 0
| _::s -> 1 + (longueur s);;
Une version recursive terminale :
# let longueur l =
let rec longrec acc l =
match l with
[] -> acc
| _::s -> longrec (1+acc) s
in
longrec 0 l
Cette fonction est predefinie en OCAML : List.length
87/103 87
Longueur d’une liste
La fonction longueur retourne la longueur d’une liste
# let rec longueur l =
match l with
[] -> 0
| _::s -> 1 + (longueur s);;
Une version recursive terminale :
# let longueur l =
let rec longrec acc l =
match l with
[] -> acc
| _::s -> longrec (1+acc) s
in
longrec 0 l
Cette fonction est predefinie en OCAML : List.length
87/103 87
Longueur d’une liste
La fonction longueur retourne la longueur d’une liste
# let rec longueur l =
match l with
[] -> 0
| _::s -> 1 + (longueur s);;
Une version recursive terminale :
# let longueur l =
let rec longrec acc l =
match l with
[] -> acc
| _::s -> longrec (1+acc) s
in
longrec 0 l
Cette fonction est predefinie en OCAML : List.length87/103 87
Evaluation de la fonction longueur
Evaluation de longueur [10;2;4]
longueur [10;2;4]
= longrec 0 [10;2;4]
[10; 2; 4] 6= [], s = [2; 4] ⇒ longrec (1+0) [2;4]
[2; 4] 6= [], s = [4] ⇒ longrec (1+1) [4]
[4] 6= [], s = [] ⇒ longrec (1+2) []
[] = [] ⇒ 3
88/103 88
Evaluation de la fonction longueur
Evaluation de longueur [10;2;4]
longueur [10;2;4]
= longrec 0 [10;2;4]
[10; 2; 4] 6= [], s = [2; 4] ⇒ longrec (1+0) [2;4]
[2; 4] 6= [], s = [4] ⇒ longrec (1+1) [4]
[4] 6= [], s = [] ⇒ longrec (1+2) []
[] = [] ⇒ 3
88/103 88
Evaluation de la fonction longueur
Evaluation de longueur [10;2;4]
longueur [10;2;4]
= longrec 0 [10;2;4]
[10; 2; 4] 6= [], s = [2; 4] ⇒ longrec (1+0) [2;4]
[2; 4] 6= [], s = [4] ⇒ longrec (1+1) [4]
[4] 6= [], s = [] ⇒ longrec (1+2) []
[] = [] ⇒ 3
88/103 88
Evaluation de la fonction longueur
Evaluation de longueur [10;2;4]
longueur [10;2;4]
= longrec 0 [10;2;4]
[10; 2; 4] 6= [], s = [2; 4] ⇒ longrec (1+0) [2;4]
[2; 4] 6= [], s = [4] ⇒ longrec (1+1) [4]
[4] 6= [], s = [] ⇒ longrec (1+2) []
[] = [] ⇒ 3
88/103 88
Evaluation de la fonction longueur
Evaluation de longueur [10;2;4]
longueur [10;2;4]
= longrec 0 [10;2;4]
[10; 2; 4] 6= [], s = [2; 4] ⇒ longrec (1+0) [2;4]
[2; 4] 6= [], s = [4] ⇒ longrec (1+1) [4]
[4] 6= [], s = [] ⇒ longrec (1+2) []
[] = [] ⇒ 3
88/103 88
Fonctions polymorphes (1/2)
Quel est le type de la fonction longueur?
longueur doit pouvoir s’appliquer a des listes d’entiers, commepar exemple
# longueur [4;3;6;1;10];;
- : int = 5
. . . alors elle doit avoir le type suivant
val longueur : int list -> int
Mais cette fonction doit aussi pouvoir etre appliquee sur une listedont les elements sont d’un autre type, comme par exemple :
# longueur [[4.5;0.3;9.8];[];[3.2;1.8]];;
- : int = 3
. . . dans ce cas la fonction longueur devrait egalement avoircomme type :
val longueur : (float list) list -> int
89/103 89
Fonctions polymorphes (1/2)
Quel est le type de la fonction longueur?
longueur doit pouvoir s’appliquer a des listes d’entiers, commepar exemple
# longueur [4;3;6;1;10];;
- : int = 5
. . . alors elle doit avoir le type suivant
val longueur : int list -> int
Mais cette fonction doit aussi pouvoir etre appliquee sur une listedont les elements sont d’un autre type, comme par exemple :
# longueur [[4.5;0.3;9.8];[];[3.2;1.8]];;
- : int = 3
. . . dans ce cas la fonction longueur devrait egalement avoircomme type :
val longueur : (float list) list -> int
89/103 89
Fonctions polymorphes (1/2)
Quel est le type de la fonction longueur?
longueur doit pouvoir s’appliquer a des listes d’entiers, commepar exemple
# longueur [4;3;6;1;10];;
- : int = 5
. . . alors elle doit avoir le type suivant
val longueur : int list -> int
Mais cette fonction doit aussi pouvoir etre appliquee sur une listedont les elements sont d’un autre type, comme par exemple :
# longueur [[4.5;0.3;9.8];[];[3.2;1.8]];;
- : int = 3
. . . dans ce cas la fonction longueur devrait egalement avoircomme type :
val longueur : (float list) list -> int
89/103 89
Fonctions polymorphes (1/2)
Quel est le type de la fonction longueur?
longueur doit pouvoir s’appliquer a des listes d’entiers, commepar exemple
# longueur [4;3;6;1;10];;
- : int = 5
. . . alors elle doit avoir le type suivant
val longueur : int list -> int
Mais cette fonction doit aussi pouvoir etre appliquee sur une listedont les elements sont d’un autre type, comme par exemple :
# longueur [[4.5;0.3;9.8];[];[3.2;1.8]];;
- : int = 3
. . . dans ce cas la fonction longueur devrait egalement avoircomme type :
val longueur : (float list) list -> int
89/103 89
Fonctions polymorphes (1/2)
Quel est le type de la fonction longueur?
longueur doit pouvoir s’appliquer a des listes d’entiers, commepar exemple
# longueur [4;3;6;1;10];;
- : int = 5
. . . alors elle doit avoir le type suivant
val longueur : int list -> int
Mais cette fonction doit aussi pouvoir etre appliquee sur une listedont les elements sont d’un autre type, comme par exemple :
# longueur [[4.5;0.3;9.8];[];[3.2;1.8]];;
- : int = 3
. . . dans ce cas la fonction longueur devrait egalement avoircomme type :
val longueur : (float list) list -> int
89/103 89
Fonctions polymorphes (1/2)
Quel est le type de la fonction longueur?
longueur doit pouvoir s’appliquer a des listes d’entiers, commepar exemple
# longueur [4;3;6;1;10];;
- : int = 5
. . . alors elle doit avoir le type suivant
val longueur : int list -> int
Mais cette fonction doit aussi pouvoir etre appliquee sur une listedont les elements sont d’un autre type, comme par exemple :
# longueur [[4.5;0.3;9.8];[];[3.2;1.8]];;
- : int = 3
. . . dans ce cas la fonction longueur devrait egalement avoircomme type :
val longueur : (float list) list -> int
89/103 89
Fonctions polymorphes (2/2)
I Les deux types precedents sont correctsI La fonction longueur a une infinite de types
Le type infere par OCAML est le plus general :
val longueur : ’a list -> int
’a (qui se lit “apostrophe a”, ou encore “alpha”) est une variable detype
Une variable de type veut dire n’importe quel type
Il faut donc lire le type de la fonction longueur comme suit :
“La fonction longueur prend en argument une liste – dont leselements sont de n’importe quel type – et retourne un entier”
90/103 90
Fonctions polymorphes (2/2)
I Les deux types precedents sont correctsI La fonction longueur a une infinite de types
Le type infere par OCAML est le plus general :
val longueur : ’a list -> int
’a (qui se lit “apostrophe a”, ou encore “alpha”) est une variable detype
Une variable de type veut dire n’importe quel type
Il faut donc lire le type de la fonction longueur comme suit :
“La fonction longueur prend en argument une liste – dont leselements sont de n’importe quel type – et retourne un entier”
90/103 90
Fonctions polymorphes (2/2)
I Les deux types precedents sont correctsI La fonction longueur a une infinite de types
Le type infere par OCAML est le plus general :
val longueur : ’a list -> int
’a (qui se lit “apostrophe a”, ou encore “alpha”) est une variable detype
Une variable de type veut dire n’importe quel type
Il faut donc lire le type de la fonction longueur comme suit :
“La fonction longueur prend en argument une liste – dont leselements sont de n’importe quel type – et retourne un entier”
90/103 90
Fonctions polymorphes (2/2)
I Les deux types precedents sont correctsI La fonction longueur a une infinite de types
Le type infere par OCAML est le plus general :
val longueur : ’a list -> int
’a (qui se lit “apostrophe a”, ou encore “alpha”) est une variable detype
Une variable de type veut dire n’importe quel type
Il faut donc lire le type de la fonction longueur comme suit :
“La fonction longueur prend en argument une liste – dont leselements sont de n’importe quel type – et retourne un entier”
90/103 90
Fonctions generiques sur les listes
91/103 91
concatenation de listes
La fonction append construit une nouvelle la liste en reunissantdeux listes bout a bout
# let rec append l1 l2 =
match l1 with
[] -> l2
| x::s -> x::(append s l2);;
val append : ’a list -> ’a list -> ’a list = <fun>
# append [2;5;1] [10;6;8;15];;
- : int list = [2;5;1;10;6;8;15]
I Cette fonction est predefinie en VERBATIM, il s’agit deList.append
I L’operateur infixe @ est un raccourci syntaxique pour cettefonction, on note l1@l2 la concatenation de l1 et l2
92/103 92
concatenation de listes
La fonction append construit une nouvelle la liste en reunissantdeux listes bout a bout
# let rec append l1 l2 =
match l1 with
[] -> l2
| x::s -> x::(append s l2);;
val append : ’a list -> ’a list -> ’a list = <fun>
# append [2;5;1] [10;6;8;15];;
- : int list = [2;5;1;10;6;8;15]
I Cette fonction est predefinie en VERBATIM, il s’agit deList.append
I L’operateur infixe @ est un raccourci syntaxique pour cettefonction, on note l1@l2 la concatenation de l1 et l2
92/103 92
Evaluation de append
Evaluation de append [1;2] [3;4]
append [1;2] [3;4]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1::(append [2] [3;4])
[2] 6= [], x = 2, s = [] ⇒ 1::2::(append [] [3;4])
[] = [] ⇒ 1::2::[3;4]
⇒ 1::[2;3;4]
⇒ [1;2;3;4]
93/103 93
Evaluation de append
Evaluation de append [1;2] [3;4]
append [1;2] [3;4]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1::(append [2] [3;4])
[2] 6= [], x = 2, s = [] ⇒ 1::2::(append [] [3;4])
[] = [] ⇒ 1::2::[3;4]
⇒ 1::[2;3;4]
⇒ [1;2;3;4]
93/103 93
Evaluation de append
Evaluation de append [1;2] [3;4]
append [1;2] [3;4]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1::(append [2] [3;4])
[2] 6= [], x = 2, s = [] ⇒ 1::2::(append [] [3;4])
[] = [] ⇒ 1::2::[3;4]
⇒ 1::[2;3;4]
⇒ [1;2;3;4]
93/103 93
Evaluation de append
Evaluation de append [1;2] [3;4]
append [1;2] [3;4]
[1; 2] 6= [], x = 1, s = [2] ⇒ 1::(append [2] [3;4])
[2] 6= [], x = 2, s = [] ⇒ 1::2::(append [] [3;4])
[] = [] ⇒ 1::2::[3;4]
⇒ 1::[2;3;4]
⇒ [1;2;3;4]
93/103 93
Concatenation rapide
I La fonction append n’est pas recursive terminaleI Si l’ordre des elements n’a pas d’importance, on peut definir
une concatenation recursive terminale qui inverse leselements de la premiere liste
let rec rev_append l1 l2 =
match l1 with
[] -> l2
| x :: s -> rev_append s (x :: l2)
val rev_append : ’a list -> ’a list -> ’a list = <fun>
# rev_append [4;2;6] [1;10;9;5];;
- : int list = [6; 2; 4; 1; 10; 9; 5]
94/103 94
Concatenation rapide
I La fonction append n’est pas recursive terminaleI Si l’ordre des elements n’a pas d’importance, on peut definir
une concatenation recursive terminale qui inverse leselements de la premiere liste
let rec rev_append l1 l2 =
match l1 with
[] -> l2
| x :: s -> rev_append s (x :: l2)
val rev_append : ’a list -> ’a list -> ’a list = <fun>
# rev_append [4;2;6] [1;10;9;5];;
- : int list = [6; 2; 4; 1; 10; 9; 5]
94/103 94
Concatenation rapide
I La fonction append n’est pas recursive terminaleI Si l’ordre des elements n’a pas d’importance, on peut definir
une concatenation recursive terminale qui inverse leselements de la premiere liste
let rec rev_append l1 l2 =
match l1 with
[] -> l2
| x :: s -> rev_append s (x :: l2)
val rev_append : ’a list -> ’a list -> ’a list = <fun>
# rev_append [4;2;6] [1;10;9;5];;
- : int list = [6; 2; 4; 1; 10; 9; 5]
94/103 94
Renverser une liste
La fonction rev pour renverser une liste l s’obtient facilement enconcatenant la liste l avec la liste vide [], en utilisant rev append
# let rev l = rev_append l [];;
val rev : ’a list -> ’a list = <fun>
# rev [4;2;6;1];;
- : int list = [1; 6; 2; 4]
95/103 95
Renverser une liste
La fonction rev pour renverser une liste l s’obtient facilement enconcatenant la liste l avec la liste vide [], en utilisant rev append
# let rev l = rev_append l [];;
val rev : ’a list -> ’a list = <fun>
# rev [4;2;6;1];;
- : int list = [1; 6; 2; 4]
95/103 95
Evaluation de rev
Evaluation de rev [1;2;3]
rev [1;2;3]
= rev append [1;2;3] []
[1; 2; 3] 6= [], x = 1, s = [2; 3] ⇒ rev append [2;3] (1::[])
= rev append [2;3] [1]
[2; 3] 6= [], x = 2, s = [3] ⇒ rev append [3] (2::[1])
= rev append [3] [2;1]
[3] 6= [], x = 3, s = [] ⇒ rev append [] (3::[2;1])
= rev append [] [3;2;1]
[] = [] ⇒ [3;2;1]
96/103 96
Evaluation de rev
Evaluation de rev [1;2;3]
rev [1;2;3]
= rev append [1;2;3] []
[1; 2; 3] 6= [], x = 1, s = [2; 3] ⇒ rev append [2;3] (1::[])
= rev append [2;3] [1]
[2; 3] 6= [], x = 2, s = [3] ⇒ rev append [3] (2::[1])
= rev append [3] [2;1]
[3] 6= [], x = 3, s = [] ⇒ rev append [] (3::[2;1])
= rev append [] [3;2;1]
[] = [] ⇒ [3;2;1]
96/103 96
Evaluation de rev
Evaluation de rev [1;2;3]
rev [1;2;3]
= rev append [1;2;3] []
[1; 2; 3] 6= [], x = 1, s = [2; 3] ⇒ rev append [2;3] (1::[])
= rev append [2;3] [1]
[2; 3] 6= [], x = 2, s = [3] ⇒ rev append [3] (2::[1])
= rev append [3] [2;1]
[3] 6= [], x = 3, s = [] ⇒ rev append [] (3::[2;1])
= rev append [] [3;2;1]
[] = [] ⇒ [3;2;1]
96/103 96
Evaluation de rev
Evaluation de rev [1;2;3]
rev [1;2;3]
= rev append [1;2;3] []
[1; 2; 3] 6= [], x = 1, s = [2; 3] ⇒ rev append [2;3] (1::[])
= rev append [2;3] [1]
[2; 3] 6= [], x = 2, s = [3] ⇒ rev append [3] (2::[1])
= rev append [3] [2;1]
[3] 6= [], x = 3, s = [] ⇒ rev append [] (3::[2;1])
= rev append [] [3;2;1]
[] = [] ⇒ [3;2;1]
96/103 96
Evaluation de rev
Evaluation de rev [1;2;3]
rev [1;2;3]
= rev append [1;2;3] []
[1; 2; 3] 6= [], x = 1, s = [2; 3] ⇒ rev append [2;3] (1::[])
= rev append [2;3] [1]
[2; 3] 6= [], x = 2, s = [3] ⇒ rev append [3] (2::[1])
= rev append [3] [2;1]
[3] 6= [], x = 3, s = [] ⇒ rev append [] (3::[2;1])
= rev append [] [3;2;1]
[] = [] ⇒ [3;2;1]
96/103 96
Recursion terminale
97/103 97
Exemple introductif
Dans un fichier somme.ml :
let rec somme n =
match n with
| 0. -> 0.
| _ -> n +. somme (n -. 1.) ;;
print_float (somme 90000.) ;;
compilation
ocamlc -o somme somme.ml
execution
./somme
Fatal error: exception Stack_overflow
98/103 98
Exemple introductif
Dans un fichier somme.ml :
let rec somme n =
match n with
| 0. -> 0.
| _ -> n +. somme (n -. 1.) ;;
print_float (somme 90000.) ;;
compilation
ocamlc -o somme somme.ml
execution
./somme
Fatal error: exception Stack_overflow
98/103 98
Exemple introductif
Dans un fichier somme.ml :
let rec somme n =
match n with
| 0. -> 0.
| _ -> n +. somme (n -. 1.) ;;
print_float (somme 90000.) ;;
compilation
ocamlc -o somme somme.ml
execution
./somme
Fatal error: exception Stack_overflow
98/103 98
Appels en attente
L’execution du programme precedent devrait correspondre auprocessus d’evaluation suivant
somme 90000.
⇒ 90000. +. somme 89999.
⇒ 90000. +. 89999. +. somme 89998.
⇒ . . .⇒ 90000. +. 89999. +. 4049865001.
⇒ 90000. +. 4049955000.
⇒ 4050045000.
L’appel a somme n est en attente du resultat de l’appel a somme (n-1)
Malheureusement, quand le nombre d’appels en attente est tropgrand le programme s’arrete !
99/103 99
Appels en attente
L’execution du programme precedent devrait correspondre auprocessus d’evaluation suivant
somme 90000.
⇒ 90000. +. somme 89999.
⇒ 90000. +. 89999. +. somme 89998.
⇒ . . .⇒ 90000. +. 89999. +. 4049865001.
⇒ 90000. +. 4049955000.
⇒ 4050045000.
L’appel a somme n est en attente du resultat de l’appel a somme (n-1)
Malheureusement, quand le nombre d’appels en attente est tropgrand le programme s’arrete !
99/103 99
Pile d’appels
Pour executer les appels de fonctions, quelque soit le langage et lecompilateur, on utilise une pile d’appel, dans laquelle on stocke(entre autre) les valeurs des arguments et l’adresse de retour del’appel
La taille de cette pile croıt donc en fonction du nombre d’appels enattente et elle ”deborde” quand il y a trop d’appels en attente(message Stack overflow)
Exemple. sous un Linux standard la taille de la pile est fixee a 8Ko
100/103 100
Appels terminaux
Dans une fonction f, un appel a une fonction g est terminal si leresultat de cet appel est le resultat de f
Exemples :
let f x = g x ;;
let f x = if ... then g x else ... ;;
let f x = if ... then ... else g x ;;
let f x = let y = ... in g y ;;
let f x = match x with ... | p -> g x | ... ;;
Une fonction est recursive terminale si ses appels recursifs sonttous terminaux
101/103 101
Appels terminaux
Dans une fonction f, un appel a une fonction g est terminal si leresultat de cet appel est le resultat de f
Exemples :
let f x = g x ;;
let f x = if ... then g x else ... ;;
let f x = if ... then ... else g x ;;
let f x = let y = ... in g y ;;
let f x = match x with ... | p -> g x | ... ;;
Une fonction est recursive terminale si ses appels recursifs sonttous terminaux
101/103 101
Appels terminaux
Dans une fonction f, un appel a une fonction g est terminal si leresultat de cet appel est le resultat de f
Exemples :
let f x = g x ;;
let f x = if ... then g x else ... ;;
let f x = if ... then ... else g x ;;
let f x = let y = ... in g y ;;
let f x = match x with ... | p -> g x | ... ;;
Une fonction est recursive terminale si ses appels recursifs sonttous terminaux
101/103 101
Appels terminaux
Dans une fonction f, un appel a une fonction g est terminal si leresultat de cet appel est le resultat de f
Exemples :
let f x = g x ;;
let f x = if ... then g x else ... ;;
let f x = if ... then ... else g x ;;
let f x = let y = ... in g y ;;
let f x = match x with ... | p -> g x | ... ;;
Une fonction est recursive terminale si ses appels recursifs sonttous terminaux
101/103 101
Appels terminaux
Dans une fonction f, un appel a une fonction g est terminal si leresultat de cet appel est le resultat de f
Exemples :
let f x = g x ;;
let f x = if ... then g x else ... ;;
let f x = if ... then ... else g x ;;
let f x = let y = ... in g y ;;
let f x = match x with ... | p -> g x | ... ;;
Une fonction est recursive terminale si ses appels recursifs sonttous terminaux
101/103 101
Appels terminaux
Dans une fonction f, un appel a une fonction g est terminal si leresultat de cet appel est le resultat de f
Exemples :
let f x = g x ;;
let f x = if ... then g x else ... ;;
let f x = if ... then ... else g x ;;
let f x = let y = ... in g y ;;
let f x = match x with ... | p -> g x | ... ;;
Une fonction est recursive terminale si ses appels recursifs sonttous terminaux
101/103 101
Execution des appels terminaux
Voici une version de somme recursive terminale
let rec somme_term acc n =
match n with
| 0. -> acc
| _ -> somme_term (n +. acc) (n -. 1.)
;;
let somme n = somme_term 0. n ;;
I Avec cette version plus de debordement de pileI Le compilateur ocamlc traite de maniere speciale les appels
terminaux : il remplace dans la pile d’appel la place occupeepar somme term acc n par l’appel somme term (n +. acc)
( n -. 1.)
102/103 102
Execution des appels terminaux
Voici une version de somme recursive terminale
let rec somme_term acc n =
match n with
| 0. -> acc
| _ -> somme_term (n +. acc) (n -. 1.)
;;
let somme n = somme_term 0. n ;;
I Avec cette version plus de debordement de pileI Le compilateur ocamlc traite de maniere speciale les appels
terminaux : il remplace dans la pile d’appel la place occupeepar somme term acc n par l’appel somme term (n +. acc)
( n -. 1.)
102/103 102
Recursion efficace
Programmer avec des accumulateurs
I Principe analogue a l’ajout de variables auxiliaires enprogrammation imperative
I Ajout de fonctions auxiliaires avec des parametressupplementaires, appeles accumulateurs
Autre exemple, la fonction factorielle en version recursive terminale
let rec fact_term acc n =
match n with
| 0 -> acc
| _ -> fact_term (n*acc) (n-1)
;;
let fact n = fact_term 1 n ;;
103/103 103
Recursion efficace
Programmer avec des accumulateurs
I Principe analogue a l’ajout de variables auxiliaires enprogrammation imperative
I Ajout de fonctions auxiliaires avec des parametressupplementaires, appeles accumulateurs
Autre exemple, la fonction factorielle en version recursive terminale
let rec fact_term acc n =
match n with
| 0 -> acc
| _ -> fact_term (n*acc) (n-1)
;;
let fact n = fact_term 1 n ;;
103/103 103