programmation fonctionnelle avancee´conchon/pfa/manpfa1.pdf · programmation fonctionnelle...

Post on 11-Sep-2018

249 Views

Category:

Documents

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Programmation fonctionnelleavancee

Notes de cours

Remise a niveau

5 septembre 2017

Sylvain Conchon

sylvain.conchon@lri.fr

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

top related