0% ont trouvé ce document utile (0 vote)
128 vues40 pages

Programmation Fonctionnelle Compress

Transféré par

Mimo
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
128 vues40 pages

Programmation Fonctionnelle Compress

Transféré par

Mimo
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
Vous êtes sur la page 1/ 40

USTL - Licence info 3ème année 2008-2009

Expression Logique et Fonctionnelle . . . Évidemment

Programmation fonctionnelle
octobre 2008
2
Chapitre 1

Programmation fonctionnelle

1.1 Programmation fonctionnelle

 Premier langage informatique fonctionnel : Lisp


(n des années 1950).
 Base théorique : λ-calcul inventé par Alonzo Church dans les années 1930 pour dénir la notion de
calculabilité.
 Nombreux langages fonctionnels : Lisp Scheme Haskell SML OCaml
, , , , , . . . qui se distinguent
selon diérents critères
 Sans ou avec eets de bord : un langage fonctionnel sans eet de bord, ou langage pur, est un
langage où l'aectation n'existe pas. Les langages purs restent assez rares, citons Haskell parmi
eux. Lisp Scheme
, , la famille des langages ML
sont impurs.
 Typés dynamiquement ou statiquement : les arguments d'une fonction doivent être d'un certain
type pour que l'évaluation d'une application puisse avoir lieu. La vérication du type, ou typage,
peut se faire à l'exécution du programme, on parle alors de typage dynamique. C'est le cas de
langages comme Lispou Scheme . Inversement, le typage peut avoir lieu avant l'exécution du
programme, lors de la compilation, le typage est alors qualié de statique. C'est le cas des langages
de la famille ML .
 Distinction entre programmation impérative et programmation fonctionnelle :
 Notions de base en programmation impérative : état d'une machine, instructions, temps (séquence),
itération. Nécessité d'avoir une connaissance minime du modèle de machine sur laquelle on pro-
gramme (modèle de Von Neumann).
 Notions de base = valeurs, expressions et fonctions. L'exécution d'un programme est l'évaluation
d'une ou plusieurs expressions, expressions qui sont souvent des applications de fonctions à des
valeurs passées en paramètre1 . Pas besoin de modèle de machine.

1.1.1 Caml
Langage utilisé : OCaml , dialecte de la famille ML
. C'est un langage développé à l'INRIA2 (http ://caml.inria.fr).
Ce langage ore
 un noyau fonctionnel ;
 des structures de contrôle impératives, ainsi que des types de données mutables ;
 la possibilité de programmation par objets.
Il est utilisé
 dans l'enseignement (classes prépa, universités) ;
 dans l'industrie (CEA, France Telecom, EDF) ;
et il est particulièrement adapté
 au prototypage d'applications ;
 aux preuves de programmes (cf Coq) ;
 compilation et interprétation.

1 On dit aussi programmation applicative


2 Institut National de Recherche en Informatique et Automatique

3
4 CHAPITRE 1. PROGRAMMATION FONCTIONNELLE

1.2 Premiers pas

1.2.1 Phrases du langage


Un programme fonctionnel écrit en OCaml
est une suite de phrases, déclarations de variables ou de
types, ou bien expressions à évaluer. L'exécution d'un programme consiste en l'évaluation de toutes les
phrases.

Les phrases du langage sont terminées par un double point-virgule ( ; ;). Ci-dessous quelques exemples
de phrases évaluées par l'interprète du langage (Le symbole # est le prompt de la boucle d'interaction, ce
qui suit est la phrase à évaluer. Les lignes débutant par le symbole - sont les résultats de l'évaluation de
la phrase).

Objective Caml version 3.09.2

# 132 ;;
- : int = 132
# 2*(45+10) ;;
- : int = 110
# 7/4 ;;
- : int = 1
# 7 mod 4 ;;
- : int = 3
# 2. ;;
- : float = 2.
# 2. +. 3. ;;
- : float = 5.
# 7./.4. ;;
- : float = 1.75
# float_of_int 2 ;;
- : float = 2.
# true ;;
- : bool = true
# true & false ;;
- : bool = false
# 1 = 2-1 ;;
- : bool = true
# "Hello" ;;
- : string = "Hello"
# "Hello " ^ "le monde " ;;
- : string = "Hello le monde "
# ’A’ ;;
- : char = ’A’
# int_of_char ’B’ ;;
- : int = 66

1.2.2 Types de base


OCaml , comme tous les langages de la famille ML
, est un langage fortement typé, à typage statique.
Avant d'évaluer une phrase, le type de l'expression est calculé, c'est l'inférence de type : contrairement
à des langages comme , C Pascal Ada
, , le programmeur n'a pas besoin de préciser le type de ces
expressions, variables ou fonctions. Le type d'une expression est calculé à partir de ses composants. Si
l'inférence de type échoue, l'expression n'est pas évaluée. Si en revanche elle réussit, l'expression peut être
évaluée.
Voici les types de base du langage :

int : les entiers. L'intervalle du type int dépend de l'architecture de la machine utilisée. C'est l'intervalle
[[−230 , 230 − 1]] sur les machines 32 bits.
1.2. PREMIERS PAS 5

Opérateur signication
+ addition
- soustraction
* multiplication
/ division entière
mod reste de la division entière
oat : les nombres ottants. Ils respectent la norme IEEE 754. OCaml distingue les entiers des ottants :
2 est de type int, tandis que 2. est de type float, et ces deux valeurs ne sont pas égales. Même les
opérateurs arithmétiques sur les ottants sont diérents de ceux sur les entiers.
Opérateur signication
+. addition
-. soustraction
*. multiplication
/. division
** exponentiation
On ne peut pas additionner un int et un float

# 2 + 3. ;;
Characters 4-6:
2 + 3. ;;
^^
This expression has type float but is here used with type int
# 2. +. 3 ;;
Characters 6-7:
2. +. 3 ;;
^
This expression has type int but is here used with type float

Fonction signication
float_of_int conversion d'un int en float
ceil partie entière supérieure (plafond)
floor partie entière inférieure (plancher)
sqrt racine carrée
exp exponentielle
log logarithme népérien
log10 logarithme décimal
... ...
string : les chaînes de caractères (longueur limitée à 264 − 6).
Opérateur signication
^ concaténation
Fonction signication
string_of_int conversion d'un int en un string
int_of_string conversion d'un string en un int
string_of_float conversion d'un float en un string
float_of_string conversion d'un string en un float
String.length longueur
char : les caractères. Au nombre de 256, les 128 premiers suivent le code ASCII.
Fonction signication
char_of_int conversion d'un int en un char
int_of_char conversion d'un char en un int

Opérateur signication
not négation
bool : les booléens pouvant prendre deux valeurs true et false. && ou & et séquentiel
|| ou or ou séquentiel
Les opérateurs de comparaison sont polymorphes, ils sont utilisables pour comparer deux int, ou
deux float, ou deux string,. . . . Mais ils ne permettent pas la comparaison d'un int et un float.
6 CHAPITRE 1. PROGRAMMATION FONCTIONNELLE

Opérateur signication
= égalité (structurelle)
<> négation de =
< inférieur
<= inférieur ou égal
> supérieur
>= supérieur ou égal
unit : le type unit est un type ne possédant qu'une seule valeur notée (). Ce type est utilisé dans la
partie impérative du langage OCaml, et par les diérentes procédures d'achage.
Procédure signication
print_int achage d'un entier
print_float achage d'un ottant
print_string achage d'une chaîne de caractères
print_char achage d'un caractère
print_newline passage à une nouvelle ligne

# print_int 12 ;;
12- : unit = ()
# print_float 3.14 ;;
3.14- : unit = ()
# print_string"Hello" ;;
Hello- : unit = ()
# print_char ’A’ ;;
A- : unit = ()
# print_newline () ;;

- : unit = ()

1.2.3 Déclaration de variables


En programmation fonctionnelle, une variable est une liaison entre un nom et une valeur. Les variables
peuvent être globales, et elles sont alors connues de toutes les expressions qui suivent la déclaration, ou
bien locales à une expression, et dans ce cas elles ne sont connues que de l'expression pour laquelle elles
ont été déclarées.
L'ensemble des variables connues d'une expression est appelé environnement de l'expression.
La syntaxe d'une déclaration globale est de la forme
let <nom> = <expr> ;;

où < nom > est le nom de la variable (en OCaml les noms de variables doivent obligatoirement
commencer par une lettre minuscule), et < expr > une expression décrivant la valeur de la variable.
# let n = 12 ;;
val n : int = 12
# n ;;
- : int = 12
# 2 * n;;
- : int = 24
# let m = 2 * n ;;
val m : int = 24
# m + n;;
- : int = 36
# m + p;;
Characters 2-3:
m+p;;
^
Unbound value p

Pour être typable, et donc évaluable, toutes les variables d'une expression doivent être dénies dans
l'environnement de l'expression, sous peine d'avoir le message d'erreur Unbound value.
1.2. PREMIERS PAS 7

On peut aussi déclarer simultanément plusieurs variables.


let <nom1 > = <expr1 >
and <nom2 > = <expr2 >

...
and <nomn > = <exprn > ;;

1.2.4 Déclaration locale de variables


Syntaxe des déclarations locales
let <nom> = <expr1 >
in <expr2 > ;;

Cette forme syntaxique permet de déclarer la variable nommée < nom > dans l'environnement d'éval-
uation de l'expression < expr2 >. En dehors de l'environnement de cette expression, la variable < nom >
n'existe pas.
Il est possible de faire des déclarations simultanées devariables locales à une expression.
let nom1 = expr1
and nom2 = expr2
...
and nomn = exprn
in exprn+1 ;;

# let pi = 3.141592
and r = 2.0
in 2. *. pi *. r ;;
- : float = 12.566368
# pi ;;
Characters 0-2:
pi ;;
^^
Unbound value pi

1.2.5 Expression conditionnelle


La syntaxe d'une expression conditionnelle est
if <expr1 > then <expr2 > else <expr3 > ;;

dans laquelle
 < expr1 > est une expression booléenne (type bool),
 < expr2 > et expr3 ont le même type.
# if 1=1 then "égaux" else "non égaux" ;;
- : string = "égaux"
# if 1=2 then "égaux" else "non égaux" ;;
- : string = "non égaux"
# if 1=2 then 0 else "non égaux" ;;
Characters 19-30:
if 1=2 then 0 else "non égaux" ;;
^^^^^^^^^^^
This expression has type string but is here used with type int

Remarques :
 en OCaml , la structure de contrôle conditionnelle est une expression et non une instruction, c'est
la raison pour laquelle les deux expressions des parties then et else doivent être du même type.
 en OCaml , il est possible d'omettre la partie else d'une expression conditionnelle. Dans ce cas,
elle est considérée comme étant la valeur () (type unit), et la partie then doit elle aussi être du
type unit.
8 CHAPITRE 1. PROGRAMMATION FONCTIONNELLE

1.2.6 Fonctions
En OCaml , comme dans tous les langages fonctionnels, les fonctions sont des valeurs comme toutes
les autres. Une variable peut donc avoir une fonction pour valeur.
# let racine_carree = sqrt ;;
val racine_carree : float -> float = <fun>
# racine_carree 2. ;;
- : float = 1.41421356237309515

Type d'une valeur fonctionnelle


Le type d'une fonction est déterminé par le type de son (ou ses) argument(s) et celui des valeurs
qu'elle renvoie. Ainsi le type de la fonction sqrt est float -> float puisqu'elle prend un float pour
paramètre et retourne un float.

Dénition d'une fonction


Le premier mot-clé permettant de dénir une fonction est function
function <param> -> <expr >

où param est le nom donné au paramètre formel, et expr est l'expression à évaluer lors d'un appel à
la fonction.
# function x -> x*x ;;
- : int -> int = <fun>

Dans l'exemple cidessus, l'évaluation de la phrase donne comme résultat une valeur fonctionnelle
de type int -> int. Mais cette fonction n'est pas nommée. Il est bien entendu possible de nommer une
fonction en déclarant une variable (globale ou non)
# let carre = function x -> x*x ;;
val carre : int -> int = <fun>

Il existe une autre façon de déclarer une variable fonctionnelle, tout à fait équivalente à la première,
et qui n'utilise pas le mot-clé function.
let f x = expr ;;

Par exemple, notre fonction carre peut être déclarée sous la forme
# let carre x = x*x ;;
val carre : int -> int = <fun>

Appel à une fonction


Pour appliquer une fonction f à une valeur v , il sut d'écrire le nom de la fonction suivi de la valeur
f v

# carre 2 ;;
- : int = 4
# carre (2 + 2) ;;
- : int = 16

Attention aux parenthèses qui permettent de contrôler la priorité des opérateurs


# carre 2+2 ;;
- : int = 6

À noter que la syntaxe plus traditionnelle


f (v )

est acceptable en Caml parce que l'expression (v) a pour valeur celle de v.
1.3. EXERCICES 9

# (2);;
- : int = 2
# carre (2);;
- : int = 4

1.2.7 Déclarations récursives


Envisageons la dénition de la fonction calculant la factorielle de l'entier passé en paramètre. Une
expression récursive classique de n! est

0! = 1
n! = n × (n − 1)! ∀n ∈ N∗
On peut être tenté de traduire cette formulation par la déclaration
# let fact n =
if n=0 then 1 else n*fact(n-1) ;;
mais on a tort car on obtient le message
Characters 37-41:
if n=0 then 1 else n*fact(n-1) ;;
^^^^
Unbound value fact
qui indique que la variable fact apparaissant dans l'expression la dénissant n'est pas liée dans l'envi-
ronnement de déclaration de la variable . . . fact. On tourne en rond.
Pour remédier à cela, il faut utiliser la forme syntaxique let rec
let rec <nom> = <expr >

dans laquelle toute référence à la variable < nom > dans l'expression < expr > est une référence à la
variable en cours de déclaration.
# let rec fact n =
if n=0 then 1 else n*fact(n-1) ;;
val fact : int -> int = <fun>
# fact 5 ;;
- : int = 120

1.3 Exercices

Exercice 1. Évaluations d'expressions


Pour la session qui suit, donnez le type et la valeur de chacune des phrases
let n1 = 10 ;;
let n2 = n1+5 ;;
let n1 = n2+6 ;;
let vrai = true ;;
vrai && false ;;
vrai && true ;;
let x = n1*n2 > n2*n2 ;;
let y = x+1 ;;
let z = 4 in z+2*z ;;
let z = let z = 4 in z+2*z ;;
let y = let x = z*z in x+2 ;;

let message = let personnage = "La gardienne "


and lieu = "du campus "
and action = "dessine"
in personnage ^ lieu ^ action ;;
print_string (message^"\n") ;;
10 CHAPITRE 1. PROGRAMMATION FONCTIONNELLE

Exercice 2. Un calcul de puissance


En remarquant que a7 = a × a2 × (a2 )2 écrivez une expression calculant 137 en 4 multiplications de
nombres entiers. Votre expression ne devra pas modier l'environnement.
Exercice 3. Signe d'un entier
Typez, puis écrivez la fonction qui à un entier positif associe 1, à un entier négatif associe -1, et à
l'entier nul associe 0.
Exercice 4. Ou exclusif
Réalisez la fonction xor qui calcule le ou-exclusif de ces deux arguments.
Exercice 5. Nombres de Fibonacci
On rappelle que les nombres de Fibonacci sont dénis par récurrence par

F0 = 0
F1 = 1
Fn+2 = Fn+1 + Fn ∀n ∈ N

Réalisez la fonction fibo de type int → int telle que la valeur de fibo n soit égale à Fn .
Exercice 6. Chaînes de caractères
En OCaml , on peut utiliser la fonction sub, prédénie dans le module String, pour extraire une
souschaîne d'une chaîne de caractères.
String.sub s a b

donne la sous-chaîne de s de longueur b démarrant à la position a (la position du premier caractère est
0).
Par exemple :
# String.sub "Expression Logique et Fonctionnelle Evidemment" 22 13;;
- : string = "Fonctionnelle"

La fonction length du module String donne la longueur d'une chaîne.


# String.length "Expression Logique et Fonctionnelle Evidemment" ;;
- : int = 46

Réalisez le prédicat est_palindrome qui teste si la chaîne passée en paramètre est un palindrome.
Exercice 7. Fonction bien dénie ?
Question 1. Quel est le type de la fonction f ? Quelles sont les valeurs qu'elle peut prendre ?
let rec f x =
if abs_float (x -. 1.) < 0.001 then true
else f ((x +. 1.) /. 2.) ;;

Question 2. Est-elle partout dénie ? Estimez en fonction de la valeur de x, le nombre d'appels récursifs.
Chapitre 2

Ordre supérieur - Polymorphisme

2.1 Ordre supérieur

L'un des points forts de la programmation fonctionnelle est de considérer les fonctions au même niveau
que toute autre type de données. À ce titre il est possible d'eectuer des traitements sur des fonctions,
et d'écrire des fonctions prenant des fonctions en paramètres et/ou produisant des fonctions en sorties.
De telles fonctions dont qualiées de fonctions d'ordre supérieur.

2.1.1 Les fonctions d'arité supérieure à 1


Toute fonction d'arité supérieure à 1 peut être considérée en OCaml comme une fonction d'ordre
supérieur, à savoir une fonction d'arité 1 à valeur fonctionnelle.
Par exemple la fonction
let add x y = x+y ;;

est de type int -> int -> int. Cette fonction peut être vue comme une fonction d'arité 2
# add 1 2 ;;
- : int = 3

La dénition précédente peut être remplacée par une autre forme équivalente
let add = function x -> function y -> x+y ;;

forme qui montre que la fonction add peut aussi être vue, et utilisée, comme une fonction d'arité 1
# add 1 ;;
- : int -> int = <fun>

On peut noter que la valeur de l'expression add 1 est du type fonctionnel int -> int, autrement dit le
résultat est une fonction qui peut elle-même être appliquée à un entier.
# (add 1) 2 ;;
- : int = 3

En fait les deux expressions add 1 2 et (add 1) 2 expriment exactement la même chose.
En Caml , toutes les fonctions sont d'arité 1.
Toute déclaration de la forme
let f x1 x2 ... xn = expr

peut être déclarée par


let f = function x1 -> function x2 -> ... -> function xn -> expr

Les fonctions à plusieurs paramètres sont en fait des fonctions à un paramètre qui retournent une valeur
d'un type fonctionnel.
Le mot-clef fun permet une écriture plus concise
let f = fun x1 x2 ... xn -> expr

11
12 CHAPITRE 2. ORDRE SUPÉRIEUR - POLYMORPHISME

Par exemple

let add = fun x y -> x+y ;;

Le type de la fonction add est int -> (int -> int) qui est synonyme de int -> int -> int.
Les types fonctionnels sont parenthésés à droite :

σ1 → σ2 → . . . σn−1 → σn = σ1 → (σ2 → . . . (σn−1 → σn ) . . .)


Attention, le type int -> int -> int est diérent du type (int -> int) -> int, comme on peut le
constater ici

# let carre x = x*x;;


val carre : int -> int = <fun>
# add carre ;;
Characters 4-9:
add carre ;;
^^^^^
This expression has type int -> int but is here used with type int

Les types fonctionnels ne sont pas parenthésés à gauche.

2.1.2 Fonctionnelles
De la même façon que le type de la valeur retournée par une fonction peut être une fonction, le type
d'un paramètre peut aussi être une fonction. Une fonctionnelle est une fonction prenant une fonction en
argument.
Par exemple, la fonction

# let demiValeurEnZero f =
(f 0) / 2 ;;
val demiValeurEnZero : (int -> int) -> int = <fun>

est une fonctionnelle qui prend pour paramètre toute fonction f de type int -> int et qui retourne la
valeur de f (0)
2 .

# demiValeurEnZero carre ;;
- : int = 0
# demiValeurEnZero (add 2);;
- : int = 1

2.2 Polymorphisme

2.2.1 Fonctions polymorphes


Une fonction peut être polymorphe.
Par exemple, la fonction

# let valeurEnZero f =
f 0;;
val valeurEnZero : (int -> ’a) -> ’a = <fun>

est polymorphe car le type de la valeur qu'elle retourne est le même que le type du résultat de la fonction
passée en paramètre.
Le caractère polymorphe de cette fonction est marqué par la variable de type ’a.
Une fonction peut aussi avoir des paramètres polymorphes.

# let zero x = 0;;


val zero : ’a -> int = <fun>
2.2. POLYMORPHISME 13

2.2.2 Typage d'une expression fonctionnelle


Voici comment calculer le type d'une expression fonctionnelle x 7→ expr dans un environnement Env .
1. Désigner par l'inconnue σ le type de x.
2. Désigner par l'inconnue τ le type de expr. L'expression focntionnelle a alors pour type σ → τ .
3. Typer chacune des sous-expressions de expr dans l'environnement Env . Des contraintes sur les
inconnues σ et τ peuvent voir le jour.
4. En tenant compte de toutes les contraintes sur σ et τ apparues dans le typage de expr, trois
situations peuvent apparaître :
(a) σ et τ sont entièrement déterminés. Le type du paramètre et celui du résultat sont connus.
(b) des contraintes contradictoires sont apparues et expr n'est pas typable. L'expression fonction-
nelle ne l'est pas non plus.
(c) σ et/ou τ ne sont pas entièrement déterminés. La fonction x 7→ expr peut convenir pour
plusieurs types de paramètres ou peut avoir plusieurs types de résultat.
Illustrons chacune des situations évoquées.

Exemple 1 : Considérons la fonction


function x -> x+a > 0

et calculons son type dans l'environnement Env =< a : int = 1 > .Env0 où Env0 est l'environnement
initial standard deOCaml .
1. soit σ le type (inconnu) de x
2. soit τ le type (inconnu) de x + a > 0
3. dans l'environnment Env ,
 a est de type int,
 + est de type int -> int -> int,
 donc x est de type int et σ =int,
 sous l'hypothèse σ =int, x + a est typable de type int,
 0 est de type int,
 > est polymorphe à valeurs booléennes
 donc x + a > 0 est typable de type bool, et par conséquent τ =bool.
4. la fonction est donc de type int -> bool.

Exemple 2 : Considérons maintenant la fonction


function x -> x && (x < 0) ;;

dans l'environnement initial de OCaml Env0 .


1. soit σ le type (inconnu) de x
2. soit τ le type (inconnu) de x&&(x < 0)
3. contraintes obtenues dans l'environnement Env
 dans Env0 , && est de type bool -> bool -> bool
 donc x doit être de type bool, σ =bool
 l'expression (x < 0) n'est alors pas typable puisque 0 est de type int diérent du type inféré
pour x
4. la fonction n'est pas typable.
ce qu'on peut vérier.
# function x -> x && (x < 0) ;;
Characters 24-25:
function x -> x && (x < 0) ;;
^
This expression has type int but is here used with type bool
14 CHAPITRE 2. ORDRE SUPÉRIEUR - POLYMORPHISME

Exemple 3 : Considérons enn la fonction


function x -> x ;;

dans un environnement quelconque Env .


1. soit σ le type (inconnu) de x
2. soit τ le type (inconnu) de x
3. on doit évidemment avoir l'équation σ = τ
4. la liste des contraintes obtenues est limitée l'équation σ = τ , donc le type de la fonction n'est pas
complètement déterminé : aucune contrainte sur le type du paramètre, le résultat a le même type
que le paramètre.

# (function x -> x) 1 ;;
- : int = 1
# (function x -> x) true ;;
- : bool = true
# (function x -> x) "elfe" ;;
- : string = "elfe"

2.2.3 Typage des fonctions récursives


Pour le typage d'une déclaration de la forme
let rec f = function x -> <expr>

dans un environnement Env , il sut de déterminer les contraintes sur les inconnues σ et τ dans l'envi-
ronnement augmenté
Env 0 =< f : σ → τ = x 7→< expr >> .Env

2.2.4 Fonctions polymorphes


Les fonctions comme celle de l'exemple 3 sont dites polymorphes car elles peuvent être appelées avec
des arguments ayant des types diérents.
Pour désigner le type d'un argument d'une fonction polymorphe, OCaml
trouve le type le plus général
et utilise des variables notées ’a, ’b, . . . pour désigner ces types.
# let id x = x ;;
val id : ’a -> ’a = <fun>

Il existe de nombreuses fonctions ou opérateurs polymorphes en OCaml, comme les opérateurs de


comparaison1 par exemple.
# (=) ;;
- : ’a -> ’a -> bool = <fun>
# (<) ;;
- : ’a -> ’a -> bool = <fun>

2.3 Exercices

Exercice 8. ou exclusif
Question 1. Réalisez la fonction xor qui calcule le ou-exclusif de ses deux arguments. Déterminez le
type de cette fonction.
Question 2. Quelles sont les natures des variables dénies par
let f = xor true;;
let g = xor false;;

1 Ces opérateurs ne s'appliquent toutefois pas sur des fonctions. L'égalité de deux fonctions est une propriété indécidable.
2.3. EXERCICES 15

Exercice 9. Sommes
Question 1. Réalisez une fonction somme1 paramétrée par les entiers a et b qui calcule bk=a k.
P
Question 2. Quel est le type de l'expression somme1 a si a est un nombre entier ?
Question 3. Réalisez une
P fonction somme paramétrées par une fonction f de type int -> int et deux
b
entiers a et b qui calcule k=a f (k). Quel est le type de cette fonction ?
Question 4. Comment utiliser la fonction somme pour dénir la fonction somme2 qui calcule la somme
des carrés des entiers de a à b, a et b étant passés en paramètres ?
Exercice 10. Expressions fonctionnelles
Donner des expressions fonctionnelles ayant les types suivants :
1. int -> int -> int
2. (int -> int) -> int
3. int -> (int -> int)
4. int -> int -> int -> int
5. int -> (int -> int) -> int
6. (int -> int) -> int -> int
7. (int -> int -> int) -> int

Exercice 11. Expressions fonctionnelles polymorphes


Donner des expressions fonctionnelles ayant les types suivants :
1. ’a -> ’a
2. ’a -> int
3. ’a -> ’b
4. ’a -> ’a -> bool
5. (’a -> ’b) -> ’a -> ’b
6. (’a -> ’b) -> (’b -> ’c) -> ’a -> ’c
7. (’a -> ’b) -> (’c -> ’b) -> ’a -> ’c -> bool
8. (’a -> int -> int) -> ’a -> ’a -> int
9. (’a -> ’b -> ’c) -> (’a -> ’b) -> ’a -> ’c

Exercice 12.
Exercice 13. Typage d'expressions fonctionnelles polymorphes
Expliquez pourquoi le type des expressions fonctionnelles qui suivent ne dépend pas de l'environnement
dans lequel elles sont évaluées, et déterminez ce type.
1. fun f x y -> f x y
2. fun f x y -> (f x) y
3. fun f x y -> f (x y)
4. fun f x y -> f (x y f)
5. fun f x y -> (f x) (y f)
6. fun f x y -> (f x) / (f y)
7. fun f x y -> x f (f y)
8. fun f x y -> f (f x y)

Exercice 14. Fonctionnelles polymorphes


Réalisez des fonctions d'ordre supérieur polymorphes pour
1. l'application d'une fonction f à un argument x
2. la composition de deux fonctions f et g
3. l'itération n fois d'une fonction f sur un argument x, ie calcul de f n (x) = f (f (. . . f (x) . . .))
16 CHAPITRE 2. ORDRE SUPÉRIEUR - POLYMORPHISME
Chapitre 3

Couples, n-uplets et listes

3.1 Couples, n-uplets


3.1.1 Construction de n-uplets :
Les n-uplets sont obtenus avec la virgule :
# 1, "a", true ;;
- : int * string * bool = (1, "a", true)

Le type des expressions ainsi construites est composé des types des diérentes composantes du n-uplet.
Comme on le voit sur l'exemple, il est possible de construire des n-uplets dont les composantes sont de
types diérents.
Un type composé n-uplet correspond à un produit cartésien d'ensembles.

Remarque : Toutefois, si le produit cartésien d'ensembles est associatif, il n'en est pas de même de la
construction des n-uplets : les types σ1 *σ2 *σ3 , (σ1 *σ2 )*σ3 et σ1 *(σ2 *σ3 ) ne sont pas égaux.
# let a1 = 1,"a",true ;;
val a1 : int * string * bool = (1, "a", true)
# let a2 = (1,"a"),true ;;
val a2 : (int * string) * bool = ((1, "a"), true)
# let a3 = 1,("a",true) ;;
val a3 : int * (string * bool) = (1, ("a", true))
# a1=a2;;
Characters 3-5:
a1=a2;;
^^
This expression has type (int * string) * bool but is here used with type
int * string * bool
# a2=a3;;
Characters 3-5:
a2=a3;;
^^
This expression has type int * (string * bool) but is here used with type
(int * string) * bool

Le type de a1 est un triplet tandis que les types de a2 et a3 sont des couples de types diérents.

3.1.2 Accès aux composantes d'un n-uplet :


On peut accéder aux composantes d'un n-uplet à l'aide d'une déclaration
# let a11,a12,a13 = a1;;
val a11 : int = 1
val a12 : string = "a"
val a13 : bool = true

17
18 CHAPITRE 3. COUPLES, N -UPLETS ET LISTES

Cette déclaration associe à trois variables les composantes du triplet a1.


Pour n = 2, il existe deux focntions prédénies donnant accès aux composantes du couple :
1. fst : ’a * ’b -> ’a donne la première (rst) composante,
2. snd : ’a * ’b -> ’b donne la première (second) composante.
Ces deux fonctions ne peuvent pas s'appliquer sur des n-uplets à plus de deux composantes.
# fst a2 ;;
- : int * string = (1, "a")
# snd a2 ;;
- : bool = true
# fst a1 ;;
Characters 4-6:
fst a1 ;;
^^
This expression has type int * string * bool but is here used with type
’a * ’b

3.1.3 Retour sur l'arité des fonctions


En mathématiques, une fonction dénie sur un ensemble A × B peut être indiéremment considérée
comme une fonction à deux variables x ∈ A et y ∈ B (arité 2), ou bien comme une fonction à une variable
c ∈ A × B (arité 1).
En informatique, les deux points de vue sont diérents.
# let add x y = x+y ;;
val add : int -> int -> int = <fun>
# let plus (x,y) = x+y ;;
val plus : int * int -> int = <fun>
# add 1 2 ;;
- : int = 3
# plus (1,2) ;;
- : int = 3
# add (1,2) ;;
Characters 4-9:
add (1,2) ;;
^^^^^
This expression has type int * int but is here used with type int
# plus 1 2 ;;
Characters 0-4:
plus 1 2 ;;
^^^^
This function is applied to too many arguments,
maybe you forgot a ‘;’

Les deux fonctions add et plus sont diérentes bien que mathématiquement semblables. On dit que
add est la forme curryée 1 de plus, et inversement, plus est la forme décurryée de add.
Entre forme curryée ou décurryée d'une même fonction, laquelle le programmeur doit-il choisir ?
Cela dépend de l'usage qui en est fait. Cependant il est toujours possible de transformer une fonction
d'une forme à l'autre.
Un argument en faveur de la forme curryée : la possibilité d'appliquer partiellement la fonction.

3.2 Listes

3.2.1 Construction de listes


OCamlpermet de manipuler des listes homogènes, c'est-à-dire constituée d'éléments du même type.
Pour construire des listes on utilise deux constructeurs : [] pour la liste vide, et : : pour l'adjonction
d'un élément en tête de liste.
1 Rien à voir avec un quelconque mélange d'épices. Du nom du logicien Haskell Curry (1900-1982)
3.2. LISTES 19

# 1 :: 2 :: 3 :: [];;
- : int list = [1; 2; 3]

Il est aussi possible de construire une liste par énumération de ses éléments sous la forme
# [1; 2; 3] ;;
- : int list = [1; 2; 3]

Remarque : La construction d'une liste par énumération de ses éléments utilise le séparateur ; à ne
pas confondre avec le séparateur ,.
# [1,2,3] ;;
- : (int * int * int) list = [(1, 2, 3)]

3.2.2 Le type list


À la diérence des n-uplets, les listes de OCaml sont homogènes.
# [1; "a"; true] ;;
Characters 4-7:
[1; "a"; true] ;;
^^^
This expression has type string but is here used with type int

OCaml précise le type d'une liste en indiquant σ list où σ est le type des éléments de la liste.

# [1 ; 2; 3] ;;
- : int list = [1; 2; 3]
# [true; false] ;;
- : bool list = [true; false]
# [(1,2) ; (3,4)];;
- : (int * int) list = [(1, 2); (3, 4)]

La liste vide peut être d'un type polymorphe si elle est construite avec le constructeur []
# [] ;;
- : ’a list = []

ou d'un type instancié si elle est obtenue par à partir d'une autre liste
# List.tl [true] ;;
- : bool list = []
# List.tl [1] ;;
- : int list = []
# List.tl ["a"] ;;
- : string list = []

3.2.3 Fonctions du module List


Les principales opérations sur les listes sont regroupées dans le module List. Pour les utiliser, il faut
préxer leur nom par le nom du module List (à moins de faire appel préalablement à la commande open
List ; ;.
Voici une liste non exhaustive des fonctions du module List.
 hd : ’a list -> ’a donne le premier élément de la liste. CU : la liste ne doit pas être vide.
# List.hd [1;2;3;4] ;;
- : int = 1
# List.hd [] ;;
Exception: Failure "hd".

 tl : ’a list -> ’a list donne la liste sans son premier élément. CU : la liste ne doit pas être
vide.
20 CHAPITRE 3. COUPLES, N -UPLETS ET LISTES

# List.tl [1;2;3;4] ;;
- : int list = [2; 3; 4]
# List.tl [] ;;
Exception: Failure "tl".

 length : ’a list -> int donne la longueur de la liste.


# List.length [1;2;3;4] ;;
- : int = 4
# List.length [] ;;
- : int = 0

 nth : ’a list -> int -> ’a donne l'élément d'indice n de la liste, le premier élément étant
d'indice 0. CU : n doit être inférieur à la longueur de la liste.
# List.nth [1;2;3;4] 0 ;;
- : int = 1
# List.nth [1;2;3;4] 3 ;;
- : int = 4
# List.nth [1;2;3;4] 4 ;;
Exception: Failure "nth".

 append : ’a list -> ’a list -> ’a list donne la liste concaténée des deux listes. L'opérateur
@ peut aussi être utilisé à la place de cette fonction.

# List.append [1;2;3;4] [5;6] ;;


- : int list = [1; 2; 3; 4; 5; 6]
# [1;2;3;4] @ [5;6] ;;
- : int list = [1; 2; 3; 4; 5; 6]

 map : (’a -> ’b) -> ’a list -> ’b list applique une fonction à tous les éléments d'une liste
pour donner la liste des résultats.
# List.map (function x -> x*x) [1;2;3;4] ;;
- : int list = [1; 4; 9; 16]

3.3 Types somme

3.3.1 Déclaration de types


Parmi les phrases du langage OCaml, certaines sont des déclarations de type. Elles commencent par
le mot-clé type.

Syntaxe :

type <nom> = <def inition> ;;

Déclarations simultanées de types


type <nom1 > = <def inition1 >
and <nom2 > = <def inition2 >
...
and <nomn > = <def initionn >

3.3.2 Constucteurs constants


On peut dénir des types en utilisant des constructeurs constants qui n'attendent pas d'argument.
Les constructeurs sont des noms débutant par une lettre majuscule.
# type couleur = Trefle | Pique | Coeur | Carreau ;;
type couleur = Trefle | Pique | Coeur | Carreau
# Trefle ;;
3.4. FILTRAGE DE MOTIF 21

- : couleur = Trefle
# Pique, Carreau ;;
- : couleur * couleur = (Pique, Carreau)
# [Trefle; Coeur; Carreau] ;;
- : couleur list = [Trefle; Coeur; Carreau]

Les types ainsi dénis sont les analogues de types énumérés de langages comme Ada Pascal
, , . . . Ils
n'ont qu'un nombre ni de valeurs correspondant à chacun des constructeurs utilisés dans l'énumération.

3.3.3 Constructeurs non constants


L'usage de constructeurs permet de dénir de nouveaux types que l'on peut considérer comme réunion
d'autres types. Le type couleur déni ci-dessus est la réunion de quatre types ayant chacun une valeur.
Avec des constructeurs non constants, on peut réunir des types existant pour n'en faire qu'un seul.
# type entier_ou_booleen = Entier of int | Booleen of bool ;;
type entier_ou_booleen = Entier of int | Booleen of bool
# Entier 1 ;;
- : entier_ou_booleen = Entier 1
# Booleen true ;;
- : entier_ou_booleen = Booleen true

Le type entier_ou_booleen ainsi déclaré réunit en un seul type le type bool et le type int. Les con-
structeurs Entier et Booleen prennent chacun un argument qui est un type placé après le mot-clé of.

3.3.4 Types récursifs


On peut faire des déclarations récursives de types, par exemple pour dénir des types listes, ar-
bres. . . L'uage du seul mot-clé type sut à la déclaration de types récursifs.
Voici par exemple la déclaration d'un type liste d'entiers
# type liste_d_entiers =
Nil
| Cons of int*liste_d_entiers ;;
type liste_d_entiers = Nil | Cons of int * liste_d_entiers
# Nil ;;
- : liste_d_entiers = Vide
# Cons (1, Nil) ;;
- : liste_d_entiers = Cons (1, Nil)
# Cons(2,Cons (1, Vide)) ;;
- : liste_d_entiers = Cons (2, Cons (1, Nil))

et celui d'un type arbre binaire dont les noeuds contiennent des entiers
# type ’a arbre_binaire =
Vide
| Noeud of ’a * ’a arbre_binaire * ’a arbre_binaire ;;
type ’a arbre_binaire =
Vide
| Noeud of ’a * ’a arbre_binaire * ’a arbre_binaire
# Vide ;;
- : ’a arbre_binaire = Vide
# Noeud (3, Vide, Vide) ;;
- : int arbre_binaire = Noeud (3, Vide, Vide)
# Noeud ("b", Vide, Noeud ("a", Vide, Vide)) ;;
- : string arbre_binaire = Noeud ("3", Vide, Noeud ("1", Vide, Vide))

3.4 Filtrage de motif

Le ltrage de motif (pattern-matching en anglais) est une remarquable et puissante caractéristique


des langages de la famille ML.
22 CHAPITRE 3. COUPLES, N -UPLETS ET LISTES

3.4.1 L'expression match ... with


Syntaxe de match

match expr with


| m1 -> expr1
| m2 -> expr2
...
| mn -> exprn

Dans une expression match l'expression expr est ltrée séquentiellement par les motifs mi . La valeur
de cette expression est celle de l'expression expri correpsondant au premier motif mi cohérent avec expr.

Exemples
1. Conversion d'une couleur en string
# let string_of_couleur c =
match c with
| Carreau -> "carreau"
| Coeur -> "coeur"
| Pique -> "pique"
| Trefle -> "trèfle" ;;
val string_of_couleur : couleur -> string = <fun>
# List.map string_of_couleur [Trefle; Carreau; Pique ; Coeur] ;;
- : string list = ["trèfle"; "carreau"; "pique"; "coeur"]

2. Le ou-exclusif
# let xor a b =
match (a,b) with
| (true,true) -> false
| (true,false) -> true
| (false,true) -> true
| (false,false) -> false ;;
val xor : bool -> bool -> bool = <fun>

Nécessité d'un ltrage exhaustif Un ltrage doit être exhaustif, c'est-à-dire envisager tous les cas.
S'il ne l'est pas, l'interprète s'empresse de le signaler par un avertissement.
# let xor a b =
match (a,b) with
| (true,true) -> false
| (true,false) -> true
| (false,true) -> true ;;
Characters 15-102:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
(false, false)
match (a,b) with
| (true,true) -> false
| (true,false) -> true
| (false,true) -> true...
val xor : bool -> bool -> bool = <fun>

Motif universel Supposons que nous voulions écrire une fonction chiffre : char -> bool qui teste
si le caractère passé en paramètre est un chire. Il serait pénible d'écrire cette fonction par ltrage de
motif en examinant chacun des 256 caractères. Il est alors judicieux d'utiliser le motif universel _ satisfait
par toutes les données.
3.4. FILTRAGE DE MOTIF 23

# let chiffre c =
match c with
’0’ | ’1’ | ’2’ | ’3’ | ’4’
| ’5’ | ’6’ | ’7’ | ’8’ | ’9’ -> true
| _ -> false ;;
val chiffre : char -> bool = <fun>
# List.map chiffre [’0’ ; ’A’ ; ’8’] ;;
- : bool list = [true; false; true]

Motif avec variables Un motif peut contenir une variable.


Voici une dénition plus concise du ou-exclusif utilisant des motifs avec variable.
# let xor a b =
match (a,b) with
| (true,b) -> not b
| (_,b) -> b ;;
val xor : bool -> bool -> bool = <fun>
Toutefois, une variable ne peut avoir qu'une seule occurrence dans un motif. Il pourrait être tentant
d'écrire l'égalité en utilisant le ltrage
# let egal x y =
match (x,y) with
| (x,x) -> true
| _ -> false ;;
Characters 38-39:
| (x,x) -> true
^
This variable is bound several times in this matching
mais il est refusé par l'interprète.

3.4.2 Filtrage de paramètres


On trouve des motifs partout :
 les déclarations de variables suivent le schéma let motif =expr, comme par exemple dans
# let a,b = 1,2 ;;
val a : int = 1
val b : int = 2
 la syntaxe des expressions function suit le schéma function motif -> expr, comme par exemple
dans
# let fst = function (x,y) -> x ;;
val fst : ’a * ’b -> ’a = <fun>
ou encore dans
# let rec fact = function
0 -> 1
| n -> n*fact(n-1) ;;
val fact : int -> int = <fun>

C'est surtout dans la dénition de fonctions à paramètres de type que le ltrage trouve son application.

Exemples
1. ltrage de liste
# let rec miroir = function
| [] -> []
| x::l -> miroir(l)@[x] ;;
val miroir : ’a list -> ’a list = <fun>
# miroir [1;2;3] ;;
- : int list = [3; 2; 1]
24 CHAPITRE 3. COUPLES, N -UPLETS ET LISTES

2. ltrage de type somme


# let rec longueur = function
| Nil -> 0
| Cons (_,l) -> 1 + longueur l ;;
val longueur : liste_d_entiers -> int = <fun>
# longueur (Cons(1,Cons(2,Nil))) ;;
- : int = 2

3.5 Exercices

Exercice 15. Fonctions fst et snd


Écrivez les fonctions prédénies fst et snd.
Exercice 16. Accès aux composantes d'un n-uplet
Question 1. Réalisez une fonction qui donne la valeur de la deuxième composante d'un triplet.
Question 2. Réalisez une fonction qui donne la valeur de la i-ème composante d'un triplet, 1 ≤ i ≤ 3
étant passé en paramètre. Quelle contrainte cela impose-t-il sur le triplet ?
Exercice 17. Currycation décurrycation
Question 1. Quelle diérence y a-t-il entre les deux fonctions
let add x y = x+y ;;
let plus (x,y) = x+y ;;

En mathématiques, une fonction dénie sur N2 peut être indiéremment considérée comme une fonc-
tion à deux variables dans N (comme la fonction add), ou comme une fonction à une variable dans N2
(comme la fonction plus).
En informatique (et en logique), les deux fonctions sont deux formes diérentes de la même fonction :
Question 2. Déterminez le type et réalisez la fonction curryfie (resp. decurryfie) qui transforme une
fonction à un argument couple (resp. à deux arguments) en sa forme curryée (resp. décurryée).
Exercice 18. Listes et premières manipulations
Pour la session qui suit, donnez le type et la valeur de chacune des phrases

let lvide = [] ;;
let lpos = [1; 2; 3] ;;
let lpos = 19::lpos ;;
let lpos = 13::42::lpos;;
let lneg = [(-1) ; (-2) ; (-3)] ;;
let lneg = [(-7) ; (-8) ; (-9)] @ lpos ;;

List.hd lpos ;; (* donne la tete de la liste *)


List.tl lpos ;; (* donne la liste sans sa tete *)
List.length lpos ;;
List.nth lpos 5 ;;
List.rev lpos ;;
List.mem 2 lpos ;; (* test d’appartenance de l’element 2 *)

let lfrag = [1 ; "chaine" ; true ];;


List.hd lvide ;; -> Uncaught exception Failure("hd")

Exercice 19. Typage de listes


Donnez le type des listes
1. []
2. [[]]
3. [[] ;[[]]]
4. [[],[[]]]
3.5. EXERCICES 25

Exercice 20. Listes et fonctions de manipulation avancées


Pour la session qui suit, donnez le type et la valeur de chacune des phrases

let lpos = [1;8;9;5;7;3;2];;

(*
* List.find : (’a -> bool) -> a’ list -> a’ = <fun>
* recherche du premier element verifiant le predicat ’a -> bool
*)

List.find (function x -> x mod 2 = 0) lpos;;

(*
* List.find_all ou List.filter : (’a -> bool) -> a’ list -> a’ list = <fun>
* recherche de tous les elements verifiant le predicat ’a -> bool
* résultat de type ’a list
*)

List.find_all (function x -> x mod 2 = 0) lpos;;


List.filter (function x -> not (x mod 2 = 0)) lpos;;

(*
* List.partition :(’a -> bool) -> a’ list -> ’a list * ’a list
* partition des elements selon un predicat ’a -> bool
* type résultat ’a list * ’a list
*)

List.partition (function x -> x mod 2 = 0) lpos;;

(* List.iter : (’a -> unit) -> ’a list -> unit


* applique la fonction ’a -> unit a chacun des elements
*)

List.iter (function x -> print_string "(" ; print_int x ; print_string ")") lpos;;

(* List.map : (’a -> ’b) -> a’ list -> b’ list


* applique la fonction ’a -> ’b a chacun des elements de la liste
* résultat de type ’b list
*)

List.map (function x -> x * x) lpos;;

(* List.flatten : ’a list list -> a’ list


* applatissement des a’ listes de listes en a’ listes
*)

List.flatten [[1;2;3];[4;5];[];[6;7]];;
List.flatten [[[1];[2;3]];[[4];[5]];[[]];[[6];[7]]];;

Exercice 21. Listes et redénition des fonctions de manipulation


Question 1. Réécrire la fonction length d'un liste en utilisant la fonction List.tl.
Question 2. Réécrire la fonction nth en utilisant les fonctions List.tl et List.hd .
Question 3. Réécrire la fonction rev à l'aide de la fonction de concatenation @ .
Question 4. Réécrire la fonction map en utilisant les fonctions List.tl et List.hd.
Question 5. Réécrire la fonction filter à l'aide des fonctions List.tl et List.hd.
Question 6. Dénissez une procédure qui ache une expression arithmétique sous forme inxée com-
plètement parenthésée.
26 CHAPITRE 3. COUPLES, N -UPLETS ET LISTES
Chapitre 4

Peut-on tout programmer avec des

fonctions ?

Le but de ces quelques notes est de montrer qu'il est possible de représenter les nombres entiers, les
booléens et les couples par des fonctions pures, et de se convaincre que tout ce qui est programmable
peut l'être avec le seul formalisme des fonctions. La base théorique sous-jacente est le λ-calcul introduit
en 1932 par Alonzo Church pour tenter de cerner la notion de calculabilité.
Tout est exprimé dans le langage Caml , en s'interdisant le recours aux nombres, booléens et n-uplets
de ce langage, ainsi qu'aux formes if et let rec, et les opérateurs ou fonctions prédénies. Les seuls
éléments du langage que nous nous autoriserons sont les identicateurs de variables, l'application d'une
fonction à un argument et la construction d'une fonction (forme function x -> ...). Par commodité,
nous nous autoriserons aussi la forme let pour nommer certains termes.

4.1 Les entiers

4.1.1 Représentation de Church


Observons la représentation des quatre premiers nombres entiers 0, 1, 2 et 3 donnée par les dénitions
suivantes :
(* l’entier zéro *)
let zero = fun f x -> x ;;

(* l’entier un *)
let un = fun f x -> f x ;;

(* l’entier deux *)
let deux = fun f x -> f (f x) ;;

(* l’entier trois *)
let trois = fun f x -> f (f (f x)) ;;

Chacune de ces dénitions indique que l'entier n est représenté comme une fonction qui itère une
certaine fonction f n fois depuis une donnée x.
Plus généralement, la représentation de Church d'un entier n ∈ N peut être donnée par l'expression
fun f x -> f (f ... (f x) ;;

dans laquelle f apparaît n fois.

Application : Cette représentation d'un entier n peut être utilisée pour itérer n fois une fonction f
depuis un élément x. Par exemple, en prenant pour fonction f la fonction prédénie succ et pour élément
x l'entier (deCaml ) 0, on a
# zero succ 0 ;;
- : int = 0

27
28 CHAPITRE 4. PEUT-ON TOUT PROGRAMMER AVEC DES FONCTIONS ?

# un succ 0 ;;
- : int = 1
# deux succ 0 ;;
- : int = 2
# trois succ 0 ;;
- : int = 3

Cela suggère la fonction de conversion d'un entier représenté sous forme de Church en un entier de
Caml.
let entier2int n = n succ 0 ;;

Remarque : le type général de la représentation de Church d'un entier est


(’a -> ’a) -> ’a -> ’a.
Toutefois, le type de zero est ’a -> ’b -> ’b et celui de un est (’a -> ’b) -> ’a -> ’b. Le type
(’a -> ’a) -> ’a -> ’a est l'unicateur le plus général de ces deux types.

4.1.2 La fonction successeur


La fonction successeur doit transformer un entier n en l'entier n+1. Lorsque les entiers sont représentés
sous forme de Church, cela revient à ajouter une application supplémentaire de la fonction intervenant
dans la représentation. Cet ajout peut se faire de deux manières :
1. on applique la fonction f au résultat de l'itération
let suc1 = fun n f x -> f (n f x) ;;

2. on applique la fonction f à l'élément x avant d'appliquer n


let suc2 = fun n f x -> n f (f x) ;;

# List.map entier2int (List.map suc1 [zero; un; deux; trois]) ;;


- : int list = [1; 2; 3; 4]
# List.map entier2int (List.map suc2 [zero; un; deux; trois]) ;;
- : int list = [1; 2; 3; 4]

Remarques :
1. Le type de la fonction suc1 est ((’a -> ’b) -> ’c -> ’a) -> (’a -> ’b) -> ’c -> ’b et celui
de la fonction suc2 est ((’a -> ’b) -> ’b -> ’c) -> (’a -> ’b) -> ’a -> ’c. Chacun de ces
types est bien plus général que ((’a -> ’a) -> ’a -> ’a) -> (’a -> ’a) -> ’a -> ’a.
2. Les entiers donnés par ces deux fonctions ne sont pas de type (’a -> ’a) -> ’a -> ’a mais de
type (’_a -> ’_a) -> ’_a -> ’_a.
# let quatre1 = suc1 trois ;;
val quatre1 : (’_a -> ’_a) -> ’_a -> ’_a = <fun>
# let quatre2 = suc2 trois ;;
val quatre2 : (’_a -> ’_a) -> ’_a -> ’_a = <fun>

La variable de type ’_a indique que le type de quatre1 est en attente d'instanciation. C'est la
première utilisation de quatre1 qui xera la valeur de ’_a.
# quatre1 ;;
- : (’_a -> ’_a) -> ’_a -> ’_a = <fun>
# quatre1 succ ;;
- : int -> int = <fun>
# quatre1 ;;
- : (int -> int) -> int -> int = <fun>
4.1. LES ENTIERS 29

4.1.3 L'addition
Pour dénir la somme de deux entiers n et m, on peut s'y prendre de deux façons :
1. on peut considérer que la somme n + m est obtenue en itérant n fois la fonction successeur sur m

let add1 = fun n m -> n suc1 m ;;

2. ou si on préfère ne pas utiliser la fonction successeur, la somme n+m peut être vue comme l'itération
n fois d'une fonction f sur un élément obtenu en itérant m fois la même fonction f

let add2 = fun n m f x -> n f (m f x) ;;

# List.map entier2int (List.map (add1 zero) [zero; un; deux; trois]) ;;


- : int list = [0; 1; 2; 3]
# List.map entier2int (List.map (add1 un) [zero; un; deux; trois]) ;;
- : int list = [1; 2; 3; 4]
# List.map entier2int (List.map (add1 deux) [zero; un; deux; trois]) ;;
- : int list = [2; 3; 4; 5]
# List.map entier2int (List.map (add1 trois) [zero; un; deux; trois]) ;;
- : int list = [3; 4; 5; 6]

4.1.4 La multiplication
Le produit de deux entiers n et m est l'itération n fois de la fonction f itérée m fois.

let mult = fun n m f -> n (m f);;

# List.map entier2int (List.map (mult zero) [zero; un; deux; trois]) ;;


- : int list = [0; 0; 0; 0]
# List.map entier2int (List.map (mult un) [zero; un; deux; trois]) ;;
- : int list = [0; 1; 2; 3]
# List.map entier2int (List.map (mult deux) [zero; un; deux; trois]) ;;
- : int list = [0; 2; 4; 6]
# List.map entier2int (List.map (mult trois) [zero; un; deux; trois]) ;;
- : int list = [0; 3; 6; 9]

4.1.5 L'exponentiation
L'exponentiation mn est encore plus simple à dénir puisqu'on l'obtient en itérant n fois l'entier m.

let exp = fun n m -> n m;;

# List.map entier2int (List.map (exp zero) [zero; un; deux; trois]) ;;


- : int list = [1; 1; 1; 1]
# List.map entier2int (List.map (exp un) [zero; un; deux; trois]) ;;
- : int list = [0; 1; 2; 3]
# List.map entier2int (List.map (exp deux) [zero; un; deux; trois]) ;;
- : int list = [0; 1; 4; 9]
# List.map entier2int (List.map (exp trois) [zero; un; deux; trois]) ;;
- : int list = [0; 1; 8; 27]

4.1.6 Et la soustraction ?
La soustraction va nécessiter de dénir les booléens et les couples.
30 CHAPITRE 4. PEUT-ON TOUT PROGRAMMER AVEC DES FONCTIONS ?

4.2 Les booléens

4.2.1 Représentation des deux booléens


Nous dénirons les deux valeurs booléennes par
(* Le vrai *)
let vrai = fun x y -> x ;;

(* le faux *)
let faux = fun x y -> y ;;

Ces dénitions se justieront par l'usage que nous en ferons pour dénir la conditionnelle.
La fonction booleen2bool convertit les termes vrai et faux en les booléens de Caml .
let booleen2bool b = b true false ;;

Remarque : Le booléen faux est représenté par le même terme que l'entier 0.

Remarque : le type de vrai est ’a -> ’b -> ’a et celui de faux ’a -> ’b -> ’b. Le type le plus
général qui unie ces deux types est ’a -> ’a -> ’a.

4.2.2 La conditionnelle
La fonction conditionnelle permet d'exprimer la forme si ... alors ... sinon .... Le choix de la
représentation des booléens rend très aisée la dénition de cette fonction.
(* la fonction conditionnelle
* cond c a s = a si c est vrai
* = s sinon
* )
let cond = fun c a s -> c a s ;;

# cond vrai 1 2;;


- : int = 1
# cond faux 1 2;;
- : int = 2

Remarque : s'il est facile de dénir la fonction conditionnelle, son utilisation pose néanmoins le prob-
lème de l'évaluation d'une expression conditionnelle. Si pour évaluer un appel de fonction, on emploie
la stratégie d'évaluation préalable de tous ses arguments, il peut arriver certaines dicultés. OCaml
adopte l'évaluation complète des arguments d'un appel de fonction et on comprend pourquoi l'évaluation
de l'expression cond vrai 1 1/0, qui vaut 1, pose problème.
# cond vrai 1 1/0;;
Exception: Division_by_zero.
Pour éviter ce genre de problème, il est nécessaire d'adopter une autre stratégie nommée évaluation
paresseuse qui consiste à n'évaluer les expressions que lorsqu'on y est obligé.

4.2.3 Les opérateurs logiques


La fonction cond permet une dénition aisée des opérateurs booléens.
(* l’opérateur de négation *)
let non = function b -> cond b faux vrai ;;

(* la conjonction *)
let et = fun b1 b2 -> cond b1 b2 faux ;;

(* la disjonction *)
let ou = fun b1 b2 -> cond b1 vrai b2 ;;
4.3. LES COUPLES 31

# List.map booleen2bool (List.map non [vrai ; faux]) ;;


- : bool list = [false; true]
# List.map booleen2bool (List.map (et vrai) [vrai ; faux]) ;;
- : bool list = [true; false]
# List.map booleen2bool (List.map (et faux) [vrai ; faux]) ;;
- : bool list = [false; false]
# List.map booleen2bool (List.map (ou vrai) [vrai ; faux]) ;;;
- : bool list = [true; true]
# List.map booleen2bool (List.map (ou faux) [vrai ; faux]) ;;;
- : bool list = [true; false]

4.2.4 Un test de nullité


On est en mesure maintenant de dénir le test de nullité d'un entier.

let estNul = function n -> n (function x -> faux) vrai ;;

# List.map booleen2bool (List.map estNul [zero; un; deux; trois]) ;;


- : bool list = [true; false; false; false]

4.3 Les couples

Par dénition, un couple contient deux choses. On doit pouvoir construire un couple à partir de
n'importe quelles deux choses, et extraire l'une ou l'autre des choses contenues dans un couple.
Le couple (x, y) peut être représenté par la fonction function z -> z x y.
La conversion de ces couples en couples de Caml s'obtient avec la fonction

let couple2caml = function c -> (c vrai),(c faux) ;;

Remarque : le type le plus général d'un couple est donc (’a -> ’b -> ’c) -> ’c.

4.3.1 Le constructeur
Le constructeur de couple est la fonction cons.

let cons = fun x y -> function z -> z x y ;;

# cons 1 2;;
- : (int -> int -> ’_a) -> ’_a = <fun>
# couple2caml (cons 1 2) ;;
- : int * int = (1, 2)

Remarque : le champ de notre étude est indépendant de la notion de type (c'est du λ-calcul pur).
Cependant, l'utilisation deOCaml amène quelques restrictions d'usage de la fonction couple2caml.
# cons 1 true ;;
- : (int -> bool -> ’_a) -> ’_a = <fun>
# couple2caml (cons 1 true) ;;
Characters 12-25:
couple2caml (cons 1 true) ;;
^^^^^^^^^^^^^
This expression has type (int -> bool -> ’a) -> ’a but is here used with type
(int -> int -> int) -> ’b
32 CHAPITRE 4. PEUT-ON TOUT PROGRAMMER AVEC DES FONCTIONS ?

4.3.2 Les sélecteurs


Les sélecteurs des composantes d'un couple s'obtiennent aisément avec les booléens.
(* sélection de la première composante *)
let car = function c -> c vrai ;;

(* sélection de la seconde composante *)


let cdr = function c -> c faux ;;

# entier2int (car (cons un deux)) ;;


- : int = 1
# entier2int (cdr (cons un deux)) ;;
- : int = 2

4.3.3 Prédécesseur d'un entier


Avec les couples il est possible de calculer le prédécesseur d'un entier n. Il sut d'itérer n fois la
fonction qui à un couple (a, b) associe le couple (b, b + 1) à partir du couple (0, 0).
(* prédécesseur
* pred n = entier qui précède n
*)
let pred = function n ->
car (n (function c -> cons (cdr c) (suc1 (cdr c))) (cons zero zero));;

# List.map entier2int (List.map pred [zero; un; deux; trois]) ;;


- : int list = [0; 0; 1; 2]

Remarque : Avec cette dénition, zero est son propre prédécesseur.

4.3.4 Soustraction
La soustraction peut se dénir par itération du prédécesseur.
let sub = fun n m -> m pred n ;;

4.3.5 Test d'égalité


Une idée naturelle pour décrire la fonction d'égalité de deux entiers est de tester la nullité de la
diérence de ces deux entiers. Mais cette idée ne peut s'appliquer en utilisant la fonction sub car elle
attribue la valeur zero à n − m lorsque n ≤ m.
let inf = fun n m -> estNul (sub n m) ;;
let egal = fun n m -> et (inf n m) (inf m n) ;;

4.4 La récursivité

4.4.1 Factorielle avec des couples


On peut calculer n! en itérant n fois la fonction (a, b) 7→ (a + 1, (a + 1)b) depuis le couple (0, 1).
let fact = function n ->
cdr (n (function c -> (cons (suc1 (car c)) (mult (suc1 (car c)) (cdr c) )))
(cons zero un)) ;;

# List.map entier2int (List.map fact [zero; un; deux; trois]) ;;


- : int list = [1; 1; 2; 6]
# entier2int (fact (exp deux trois)) ;;
- : int = 362880
4.4. LA RÉCURSIVITÉ 33

4.4.2 Point xe


On imagine bien pouvoir écrire, de la même façon que la fonction fact ci-dessus, toute fonction
que l'on programmerait autrement à l'aide d'une boucle pour1 . Mais qu'en estil pour les fonctions qui
n'entrent pas dans ce cadre2 ?
Nous allons proposer une autre solution pour la fonction factorielle qui suit un schéma général ap-
plicable à toute fonction. Cette approche est celle du calcul du plus petit point xe d'une fonctionnelle,
point xe qui peut être déterminé par un opérateur nommé combinateur de point xe.

L'ensemble ordonné F
Notons F = NN l'ensemble de toutes les fonctions de N dans N. Les fonctions considérées ici ne sont
pas nécessairement dénies sur N tout entier, c'estàdire qu'étant donné f ∈ F , il peut exister des
valeurs de n ∈ N telles que f (n) ne soit pas déni. Nous noterons ⊥ la (seule) fonction de F dénie nulle
part.
Voici écrite en Caml3 la fonction ⊥ :

let bottom = function x ->


(function y -> y y) (function y -> y y);;

Toute tentative d'application de la fonction bottom à un quelconque argument conduit à une évaluation
innie.
# bottom 1 ;;
Interrupted.

On dira qu'une fonction f ∈ F est moins dénie qu'une fonction g ∈ F , et on notera f 4 g , si


pour tout entier n où la fonction f est dénie, on a f (n) = g(n), autrement dit si f et g coincident sur
l'ensemble de dénition de f . La relation 4 sur l'ensemble F en fait un ensemble non totalement ordonné
admettant la fonction ⊥ comme plus petit élément, et les fonctions partout dénies comme éléments
maximaux.

Fonctionnelles
Nous appellerons fonctionnelle toute application de F dans lui-même. Par exemple, la fonctionnelle
Φfact dénie par
Φfact : F −→ F
f 7−→ g = Φfact (f )
où g est la fonction dénie par
1 si n = 0

g(n) = .
n × f (n − 1) sinon
Appliquons Φfact à la fonction ⊥
f1 = Φfact (⊥).
Par la dénition même de Φfact , la fonction f1 est dénie en 0 et vaut 1. Et elle n'est dénie nulle part
ailleurs, étant donné que ⊥ n'est dénie nulle part.
Appliquons maintenant Φfact à f1
f2 = Φfact (f1 ).
Cette fonction f2 est dénie en 0 où elle prend la même valeur que f1 , et en 1 où elle vaut 1. Ainsi f1 est
moins dénie que f2 . D'autre part, f2 n'est dénie pour aucun entier supérieur à 1.
Si on considère la suite de fonctions (fn )n∈N de F dénie par récurrence par
f0 = ⊥
fn+1 = Φfact (fn )
on obtient des fonctions telles que
1 Dans la terminomogie des fonctions récursives, ce sont les fonctions primitives récursives
2 Comme par exemple, le calcul du pgcd de deux entiers, ou encore le calcul de la longueur d'une liste.
3 Par défaut, OCaml refuse la dénition de bottom car l'expression y y n'est pas typable. Il faut autoriserles types
récursifs (option -rectypes de la commande ocaml) pour que Caml accepte cette dénition en lui donnant alors le type ’a
-> ’b.
34 CHAPITRE 4. PEUT-ON TOUT PROGRAMMER AVEC DES FONCTIONS ?

1. fn (k) = k! si k < n ;
2. et fn (k) non déni si k ≥ n.
On peut remarquer que cette suite est constituée de fonctions de mieux en mieux dénies

f0 4 f1 4 f2 4 . . . 4 fn 4 . . .

autrement dit la suite est croissante dans F , et que la fonction factorielle (fact) est mieux dénie que
toutes les fonctions de cette suite,
∀n ∈ N fn 4 fact.

On peut dénir tout cela en Caml4 par


let phiFact = fun f n ->
if n=0 then 1 else n * f (n - 1) ;;
let f0 = bottom ;;
let f1 = phiFact f0 ;;
let f2 = phiFact f1 ;;
let f3 = phiFact f2 ;;
let f4 = phiFact f3 ;;

et on peut vérier que les fonctions ainsi dénies sont des fonctions qui approchent de mieux en mieux
la fonction factorielle.

# List.map f2 [0;1] ;;
- : int list = [1; 1]
# List.map f3 [0;1;2] ;;
- : int list = [1; 1; 2]
# List.map f4 [0;1;2;3] ;;
- : int list = [1; 1; 2; 6]
# f4 4 ;;
Interrupted.

Point xe
Quelle est la fonction obtenue si on applique la fonctionnelle Φfact à la fonction fact ? On peut vérier
que l'on a
Φfact (fact) = fact.

Trichons un peu et vérions cette égalité :

# let rec fact n =


if n=0 then 1
else n*fact(n-1) ;;
val fact : int -> int = <fun>
# List.map fact [1;2;3;4;5;6;7;8;9;10] ;;
- : int list = [1; 2; 6; 24; 120; 720; 5040; 40320; 362880; 3628800]
# let fact2 = phiFact fact ;;
val fact2 : int -> int = <fun>
# List.map fact2 [1;2;3;4;5;6;7;8;9;10] ;;
- : int list = [1; 2; 6; 24; 120; 720; 5040; 40320; 362880; 3628800]

Autrement dit, la fonction fact est un point xe de la fonctionnelle Φf act .


Une fonction f ∈ F est un point xe d'une fonctionnelle Φ si on a l'égalité

Φ(f ) = f.

4 Nous abandonnons les contraintes xées au début de ce document an de permettre une meilleure lecture du code et
faciliter la compréhension.
4.5. EXERCICES 35

Remarque : Deux questions naturelles se posent


1. est-ce que toute fonctionnelle admet des points xes ?
2. y a-t-il unicité du point xe ?
La réponse à ces deux questions est négative en général. L'existence est assurée si la fonctionnelle satisfait
une propriété de monotonie, c'estàdire si Φ(f ) 4 Φ(g) dès lors que f 4 g 5 . Φfact est une fonctionnelle
monotone, et la fonction fact est donc son (plus petit) point xe.

Combinateur de point xe


Existe-t-il un moyen de calculer le point xe d'une fonctionnelle (s'il existe). La réponse est armative,
et il existe plusieurs opérateurs (ou fonctions ou algorithmes) permettant de les calculer. On les appelle
combinateurs de point xe.
En voici un, nommé combinateur de Curry, dont nous donnons la dénition en Caml .
let fixCurry = function f ->
(function x -> f (x x)) (function x -> f (x x)) ;;

Vérions que fixCurry est un combinateur de point xe. Soit Φ une fonctionnelle.

fixCurry Φ = (function f -> (function x -> f (x x)) (function x -> f (x x))) Φ


→ (function x -> Φ (x x)) (function x -> Φ (x x))
→ Φ ((function x -> Φ (x x)) (function x -> Φ (x x)))
= Φ (fixcurry Φ).

La èche → indique une étape de réduction.


Ainsi, le terme fixCurry Φ est un point xe de Φ.
Modions la dénition précédente de fixCurry pour le rendre opérationnel en Caml.
let fixCurry = function f ->
(fun x y -> f (x x) y) (fun x y -> f (x x) y) ;;

Appliquons ce combinateur à Φfact


let fact = fixCurry phiFact ;;

et vérions le point xe obtenu


# List.map fact [1;2;3;4;5;6;7;8;9;10] ;;
- : int list = [1; 2; 6; 24; 120; 720; 5040; 40320; 362880; 3628800]

Exercice 22. Dénir de façon analogue les fonctions


1. bonacci
2. pgcd de deux entiers
3. longueur d'une liste
4. concaténation de deux listes.

4.5 Exercices

5 pour en savoir plus à ce sujet cf A COMPLETER


36 CHAPITRE 4. PEUT-ON TOUT PROGRAMMER AVEC DES FONCTIONS ?
Bibliographie

[Cha00] Emmanuel Chailloux, Pascal Manoury et Bruno Pagano Développement d'applications avec
Objective Caml O'Reilly, 2000
[Narb05] Philippe Narbel Programmation fonctionnelle, générique et objet Vuibert, 2005

37
38 BIBLIOGRAPHIE
Table des matières

1 Programmation fonctionnelle 3
1.1 Programmation fonctionnelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.1 Caml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Premiers pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.1 Phrases du langage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.2 Types de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.3 Déclaration de variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.4 Déclaration locale de variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.5 Expression conditionnelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.6 Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.7 Déclarations récursives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2 Ordre supérieur - Polymorphisme 11


2.1 Ordre supérieur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.1.1 Les fonctions d'arité supérieure à 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.1.2 Fonctionnelles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2 Polymorphisme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.1 Fonctions polymorphes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.2 Typage d'une expression fonctionnelle . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2.3 Typage des fonctions récursives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.2.4 Fonctions polymorphes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.3 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3 Couples, n-uplets et listes 17


3.1 Couples, n-uplets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.1 Construction de n-uplets : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.2 Accès aux composantes d'un n-uplet : . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.3 Retour sur l'arité des fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2 Listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2.1 Construction de listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2.2 Le type list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.2.3 Fonctions du module List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.3 Types somme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3.1 Déclaration de types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3.2 Constucteurs constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3.3 Constructeurs non constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.3.4 Types récursifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.4 Filtrage de motif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.4.1 L'expression match ... with . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.4.2 Filtrage de paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.5 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

39
40 TABLE DES MATIÈRES

4 Peut-on tout programmer avec des fonctions ? 27


4.1 Les entiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.1.1 Représentation de Church . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.1.2 La fonction successeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.1.3 L'addition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.1.4 La multiplication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.1.5 L'exponentiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.1.6 Et la soustraction ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.2 Les booléens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.2.1 Représentation des deux booléens . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.2.2 La conditionnelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.2.3 Les opérateurs logiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.2.4 Un test de nullité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.3 Les couples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.3.1 Le constructeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.3.2 Les sélecteurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.3.3 Prédécesseur d'un entier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.3.4 Soustraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.3.5 Test d'égalité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.4 La récursivité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.4.1 Factorielle avec des couples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.4.2 Point xe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.5 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

Vous aimerez peut-être aussi