Remarques : Vous trouverez ici tous les cours d'informatique du second semestre a l'exception de ceux des deux dernières semaines.

Informatique

Le langage CAML est un dérivé du langage LISP.

La Programmation fonctionnelle
Plan:
Chp 1 - Les concepts fondamentaux
Chp 2 - Les fonctions
Chp 3 - Les types fondamentaux
Chp 4 - Les listes
Chp 5 - Récursivité, récurrence


Chp 1 - Les concepts fondamentaux
I - La programmation fonctionnelle
1) Les modes de l'impératif et du fonctionnel
Les langages impératifs sont fondés sur la notion d'ETAT de la machine (mémoire centrale et périphériques).
- Ecrire un programme dans un langage impératif
Décrire la suite des états par lesquels la machine doit passer, en partant d'un état initial jusqu'à un état final, de telle sorte que l'exécution du programme favorise les résultats recherchés.
=> On explique comment le réaliser.
- Exemple : Algo impératif et Pascal
Les langages fonctionnels sont fondés sur la notion de fonction.
=> Ecrire un programme dans un langage fonctionnel
Décrire une fonction qui, appliquée à des arguments, renvoie le résultat recherché.
- Exemple : Algo fonctionnelle et CAML
Remarque : Il n'y a pas de barrières infranchissables entre "impératif" et "fonctionnel"
- "Impératif" avec "fonctions" est proche du "fonctionnelle".
- "fonctionnelle" a besoin de l'affectation.

2) Principe de base
Programmer dans un langage fonctionnel consiste :
- à construire des définitions
- à utiliser la machine pour évaluer des expressions
=> 2 acteurs : le programmateur et la machine
Rôle fondamental de chaque acteur
a) Le programmeur
Construction d'une fonction résolvant un problème donné. Ce travail engendre en général un certain nombre de fonction subsidiaires.
Ce travail va être exprimé dans une notion qui obéira aux principes mathématiques usuels.
b) La machine
. Doit agir comme un évaluateur ou calculateur
. Elle doit évaluer (ou calculer) des expressions et afficher des résultats trouvés
. Ce calculateur fonctionnel évalue les expressions qui contiennent des occurences de noms de fonctions (définies par le programmeur) en utilisant comme règles de réduction (ou de simplification) les définitions posées afin de les transformer au format affichable.

II - Session et script
1) La programmation fonctionnelle en mode interactif
Elle va commencer par évaluer l'idée d'utilisation d'un emachine comme calculateur fonctionnel en mode interactif. a) Affichage d'un signal d'invite (ou prompt)
La machine indique qu'elle est prête à évaluer une expression en affichant un signal d'invite.
Nous conviendrons par convention qu'il s'agit de ?
b) Evaluation d'une expression en mode interactif
On entre au clavier une expression. On la transmet à l'évaluateur en la faisant suivre d'"Enter". Le calculateur répond en affichant le résultat de l'évaluation de l'expression. Le calculateur réaffiche un nouveau signe d'invite à un enouvelle expression.
- Remarques : Pour certains langages, l'expression doit se terminer par certains signes. Ainsi en CAML, on doit la faire suivre par;;

2) Notion de session
Définition : On appelle session une séquence d'interaction entre le programmeur et le calculateur.
Exemple de session:
?49 <- cas le plus simple. Pas d'évaluation à faire
49
?6*7 <- La simplification se fait en effectuant la multiplication
42
? (7*9) +(2*5) <- La simplification se fait en effectuant la * puis l'+
73
Remarques : Les règles d'évaluation des expressions seront détaillées au chap 3.

III - La notion d'environnement ou programme fonctionnel
Pour l'instant, une session est simple :on évalue seulement des expressions. Il serait plus intéressant de disposer d'un moyen permettant de NOMMER des VALEURS afin de pouvoir les utiliser ensuite.
1) Définition et liaison
- Définition : On appelle définition la construction qui permet de nommer une valeur.
Convention : le nommage des valeurs va se faire en utilisant = (au sens d'équation)
définition : dudule = 7
Un identificateur sert à désigner une valeur. La valeur est dite liée à l'identificateur par la définition posée : identificateur = expression
Le couple (identificateur / valeur) est appelé une liaison.
Exemple:
u = 5
v = 2 + 1 + 7

2) Environnement
On appelle environnement (ou contexte ou script) un ensemble de définitions (c.a.d des liaisons).
Exemple de script simple : x = 1
identificateur y = 2 valeur
L'environnement est caractérisé par les deux liaisons :
- l'identificateur x est lié à 1 et <- (x,1)
- l'identificateur y est lié à 2 et <- (y,2)
Exemple de script :
identificateur | argument | définition | expressions | conditions
carré                   x             =            x*x
min                   x,y             =            x                   si x < y
                                          =           y                  si x>y
Dans ce script, deux fonctions identifiées par "carré" et "min" ont été définies :
- La fonction "carré" prend la valeur de x comme argument et renvoie la valeur x*x.
- La fonction "min" prend comme argument x et y et renvoie la valeur minimum.
Remarque: On ne s'intéresse pas encore ici à la syntaxe correcte des définitions. Notons toutefois qu'elles sont écrites comme étant des équations entre certains types d'expressions.
Un script ayant été créé, on peut le soumettre au calculateur et donc entrer une session.
Exemple : ayant défini l'environnement précédent:
? carré (3+4)
49
? min 5 7
5
? carré (min 8 11)
64
?
Remarque: Dans l'environnement: ? t-t erreur car l'identificateur t n'est pas lié à une valeur.

3) Portée d'un identificateur
Principe fondamental en algo fonctionnel. La portée d'un identificateur est la durée de validité de la liaison qui lui est associée.
Exemple:
année = 1995 liaisons
(année,1995)
année future = année + 1 (année future,196)
? année future
1996
année = 0 (année,0) <- année est liée à zéro
? année + 5
5
? année future <- La valeur liée à "année future" n'est pas modifiée,
1996 car dans sa définition, il y a eu remplacement textuel de
"année". Mais (année,1995) n'est plus accessible. Désormais
"année" est liée à "0".
Environnement=[(année,0),(année future,1996),(année,1995),Env]
-Définition :
. Le script soumis à l'évaluateur définit l'environnement global de la session qui l'utilise.
. Les liaisons que crée le script sont appelées des liaisons globales.
Remarque fondamentale: Une déclaration globale n'est pas modifiable.
-> Elle reste valide pendant toute la session
-> Si on veut changer la valeur liée à l'identificateur dans l'environnement global, on doit obligatoirement définir une nouvelle liaison globale qui sera désormais seule accessible.
-> Les déclarations utilisant l'ancienne liaison ne sont pas affectées.

4) CAML et les sessions
a) Le script des définitions globales
obligatoire
let i arg = expressions;;
=soit
*facultatif
->forme plus ou moins complexe
b) La session en CAML # let x=5;
# x:int=5 : dont le type est
# x;;
_ : int = 5
# (La foncton carré : x->x*x avec x entier)
# let carré x = x*x;; * = multiplication des entiers
# carré:int -> int = < fun >
# carré x;;
_ : int = 25
# let min x y = if x<=y then x
else y;;
_ : int = 25
#carré (-5);;
#min : int -> int -> int = < fun >
#carré (min 2 4);;
_ : int = 4
#

IV - L'analyse des expressions
1) La réduction
- Définition : On appelle réduction (ou simplification ou évaluation), le processus utilisé par la machine pour évaluer une expressions, en la réduisant à la forme équivalente la plus simple et pour afficher ensuite sa valeur.
convention : => signifie "réduit à"
Exemple : Règle de simplification
i) carré (5+7) => carré 12 (+)
=> 12*12 (carré)
=> 144 (*)
ii) carré (5+7) => (5+7)*(5+7) (carré)
=> 12 * (5+7) (+)
=> 12 * 12 (+)
=> 144 (*)

L'évaluation de l'expression est faite par un processus simple de substitution et de simplification, utilisant à la fois:
- des règles prédéfinies (des primitives), et
- des règles édictées par le programme sous forme de définitions.

- Définition : . Une expression est dite sous forme canonique normale si elle ne peut plus être réduite.
. Une valeur affichable comme étant sa représentation canonique normale.
Remarque: Les termes valeurs n'ont pas de forme canonique normale. Par exemple, la valeur Pi.
- Le symbole du T inversé
(***il n'existe pas dans le jeu de caractère ISO-8859 du HTML. A moins d'insérer une image en plein milieu du texte (ce qui serait peu judicieux), il n'y a pas moyen de vous le représenter***)
Certaines expressions n'ont pas la possibilité d'être réduites : 1/0
Suivant le langage arithmétique choisi, l'évaluateur voit sortir un message d'erreur ou "se" lancer dans un processus infin de calculs.
Convention: On peut introduire le symbole du T inversé représentant une valeur indéfinie.
=> Il n'y aura pas d'exception. Ainsi 1/0= T inversé

2) L'univers des types
L'univers des valeurs est partitionné en ensembles organisés appelés des types de valeur.
Deux catégories:
i)Les types de base (ou prédéfinis)
Les valeurs d'un type de base sont dérivables et les opérations possibles de ce type sont précisées.
- Le type num désignant les nombres
- Le type bool désignant les valeurs dérivables
- Le type car désignant les caractères
- Le type chaîne désignant les chaînes de caractères
Remarque : En CAML, on distingue les entiers (type int) des réels (type float). CAML distingue alors les opérations de ces deux types (exemple: + pour int et +. pour float).
ii) Les types construits (ou composés)
Leurs valeurs sont construites à partir de type déjà défini
exemple :
a) (num,car) paires dont la première valeur est un nombre et la deuxième un caractère.
b) (num -> num) f numérique
c) [car] liste de caractères
Remarque : A chacun de ces types, il faudra associer un certain nombre d'opérations.

3) Le principe du typage fort
Définition: Une expression est bien formée si on peut lui assigner un type qui se déduit uniquement de ces constituants. Dans le cas contraire, l'expression est mal formée.
Exemple : Considérons le script
valeur x = 'A'
forme x = x + valeur
avec x de type num, valeur de type car et + impliquant une addition num
=> L'expression (x+valeur x) est mal formée
=> Le script est rejeté

4) Les deux niveaux d'analyse d'une expression
Il y a deux niveaux d'analyse lorsque un eexpression est soumise à l'évaluation:
i) L'analyse syntaxique:
L'évaluateur vérifie que l'expression satisfait les lois de définition d'une expression.
- si oui, on passe au niveau 2
- si non, une erreur de syntaxe est signalée
ii) L'analyse de type
L'évaluateur vérifie que l'expression possède un type.
- si non, une erreur est signalée
- si oui, le processus d'évaluation commence

5) Les deux niveaux d'analyse d'un script
Même démarche jusqu'en ii). Dans ce cas et seulement, le script est accepté.
Exemple en CAML:
#let carré x = x * x;;
#carré : int -> int = < fun >
#carré (-2);;
_ : int = 4
#carré -5;;
>erreur
>expression of type int -> int
#21+5.2;;
>erreur
>expression of type float
>cannot be used with type int
#

V - Spécification et implémentation
Définition :
a) Une spécification est une description du travail que le programme doit accomplir.
b) Une implémentation est un algo qui réalise la spécification.
=> Elles sont différentes par nature et servent des objectifs différents.
a) Spécification=expression des intentions du programmateur
Objectif = expression claire et succinte
b) Implémentation=expression pour l'exécution par la machine
Objectif = expression efficace afin que le programme s'exécute dans le temps et l'espace disponible.
Exemple : la fonction "plus_que_carré"
*Une spécification de plus_que_carré:num -> num peut-être : Pour tout x>=0, plus_que_carré x > carré x
*Une implémentation peut-être : plus_que_carré x = carré (x+1)
2 aspects : . Elle est valide dans notre notation si on connaît la définition de carré x
. Elle satisfait la spécification:
plus_que_carré x = carré (x+1) = (x+1)(x+1)
          = x*x + 2*x+1
          > x*x
          > carré x
Remarque: La spécification n'est pas en général unique. On peut citer deux méthodes permettant de vérifier que des définitions satisfont les spécifications.
a- Poser la définition puis vérifier que les conditions sont requises (exemple précédent)
b- Développer systématiquement la définition
-> comme x+1>x alors carré x+1>carré x
On peut choisir: plus_que_carré x = carré x+1

Paradygme et développement d'un logiciel
1- Ecrire d'abord une spécification claire
2- Développer ensuite une implémentation raisonnablement efficace


Chp 2 - Les fonctions
I - Type fonctionnel et valeur fonctionnelle
Il s'agit du plus important type de valeur en programmation fonctionnelle. Les types prédéfinis utilisés ici seront vus en détail au chapitre 3.
1) Les fonctions mathématiques
Définition : Etant donné deux ensembles A et B, une fonction f définie dans A et à valeur dans B, et une loi de correspondance qui fait correspondre à tout élément de A, un de B. Quand f(x) existe, il est appelé le résultat de l'application de f à x.
2) Définition d'une fonction en maths
a) Le graphe représentatif: = {x,f(x)|x appartient à A}
b) Par abstraction: expression = 5-(3+x)²
        x --> expression f(x)=5-(3+x)²
c) Utilisation d'une structure alternative
abs (x) = x si x>=0
            = -x si x<=0
Par morceaux:
X(x) = x^3-1 si x <=1
         = x²+4 si x>1 et <=10
          =x-x si x>10 et <=1000
3) Les fonctions en programmation fonctionnelle
Définition: Etant donné deux types A et B, une fonction f définie dans A à valeur dans B et une loi de correspondance qui associe à toute valeur prise dans A une valeur dans B.
On exprime cette information sous la forme : f:A->B
Cette formule indique que le type de f est A -> B
La fonction f:A->B est : - prendre ses arguments dans A
- renvoyer un résultat dans B

Remarque:. Le symbole -> dénote un opérateur sur les types
. Il sert à construire de nouveaux types: LES TYPES FONCTIONNELS
Ainsi A->B dénote le type fonctionnelle des fonctions de A et B
Une valeur ayant un type fonctionnelle est appelée une fonction ou une valeur fonctionnelle.

Remarque: Il faut distinguer une fonction f de f(x), son application à un argument:
. f est une valeur prise dans le type fonctionnel A dans B
. f(x) est unue valeur de type B

Scripts de définitions de fonctions:

carre:num -> num (type de fonction)
carre x=x*x (Moyen de calcul de f(x))

double:num -> num
double x=2*x

Remarque: Notre langage fonctionnel est fortement typé, il n'est pas nécessaire d'accompagner les définitions de fonctions de leurs types. Celui-ci est directement déduit de l'équation de définition.
On verra que x, un opérateur sur le type num -> (num->num) est implicite.

4) Les paramètres de type
. La fonction id x=x
. Son type est A -> A quelque soit le type A
. Pour assigner à "id" un type sensé, on introduit alpha appelé: PARAMETRE DE TYPE
=> On dira alors que id est de type alpha -> alpha
Pour tout type A, id peut-être considéré de type A -> A
Convention : les paramètres de ce type seront alpha,beta,gamma

5) Vers un langage des expressions dénotant les types
Nous avons à notre disposition, le noyau de ce langage:
- les expressions constantes (num,car...)
- les paramètres de types (alpha,beta...)
- les opérateurs (-> ...)
Si une expression contient des paramètres de types, on dit qu'elle dénote un type polymorphe sinon, elle dénote un type monomorphe.

II - Déclaration de fonction
1) La formule de calcul est unique
identificateur paramètres = expression
On peut utiliser des paramètres (par1, par2...)
exemple :
carre x = x * x
produit (a,b) = a* b
somme x y = x+y

2) Le calcul de la valeur est fait par séparation des cas
identificateur paramètres = expression1, si condition
          = expression2, si condition
................
          = expression, si condition
->expression ->valeurs booléennes
Exemple:
min x y = x, si x<=y
        = y, si x>y
morceaux x = x+1, si x<=1
        = x+2, si x>1 et <=100
        = x+3, si x>100
Remarque:
. L'évaluation des conditions est faite de façon linéaire de la première à la dernière posée.
. Les fonctions sont évaluées par la première condition vraie rencontrée.
. Si la liste des conditions n'est pas exaustive, on pourra engendrer un erreur (valeur non définie) à l'utilisation.

3) L'alternative simple
identificateur paramètres=expression1, si condition
          =expression2, sinon
exemple:
max x y = x, si x>=y
      = y, sinon

III - Les définitions locales
En math, une définition est parfois introduite sous la forme:
f(x,y)=(a+1)*(a+2) sachant que a=(x+y)/2
L'expression f(x,y) introduit un edéfinition locale dont le contexte est l'expression à droite du = dans la définition de f.

1) Pas de séparation des cas
identificateur paramètres = expression1 (contenant u) sachant que u=expression2
Exemple:
f x y = (a+1)*(a+2) sachant que a = (x+y)/2

2) Séparation des cas
identificateur paramètres=expression1, si condition1
...................
          =expression2, si condition2
sachant que u = expression ?
Remarque: la portée de la définition locale est l'ensemble des cas
Exemple:
f x y = x + a si x>10
      = x - a sinon
sachant que a=carre (y+1)

Remarques:
i) On peut avoir plusieurs définitions locales indépendantes.
On notera alors:
      sachant que définition locale 1
(et implicite) définition locale 2
La règle de portée est la même et de façon indépendante les unes des autres.
ii) Une définition locale étant une définition, elle peut en contenir d'autres.
Exemple:
t = x + y + z | 26
sachant que z = x + y | 13
sachant que y = 3 + x | 8
sachant que x = 5 | 5
? t
26
?

3) Définitions locales en CAML
|Expression
|sachant que id f=expression

|Expression
|sachant que id f1=expression1
| id f2=expression2

|Expression
|sachant que id f2=expression2
| sachant que id f1=expression1

ALGORITHME

|let id f=expression1 in expression

|let id f1=expression1
|and id f2=expression2
|in expression

|let id f1=expression1
| in
|let id f2=expression2
| in
| expression

CAML

#let t=let x=5
  in let y=3+x
   in let z=y+z
      in x+y+z;;
t:int=26
# t;;
_ : int=26

4) Les fonctions en CAML
let min x y = if x<=y then x
else y;;
let mini = function (x,y)
if x <= y then x
else y;;

Exemple: let carre x = x*x;;
let puis 4 x =let y = carre y
in carre y;;

IV - Les définitions récursives
1) Les fonctions récursives en programmation fonctionnelle
Une définition est récursive si elle fait appel à elle-même dans sa définition ou si elle fait appel à d'autres définitions qui font au moins appel une fois à sa définnition.

Exemple : calcul de n!
0!=1
n!=n(n-1)!

fact n = 1 si n=0
= n * fact (n-1) sinon

2) Les fonctions récursives en CAML
On doit ajouoter le mot-clé rec dans la définition après le mot-clé let.
Exemple:
. let rec fact n =
if n=0 then 1
else n * fact (n-1);;
. let rec fact = function n ->
if n=0 then 1
else n*fact (n-1);;

Chp 3 - Les types fondamentaux

I - Introduction
. Dans ce chapitre, on va introduire les types de valeur de base à l'aide desquels on va construire les expressions.
. Nous verrons quelle est la représentation de chacun d'entre-eux et nous donnerons quelques primitives permettant de les manipuler.

II - Les nombres (num)
1) Les valeurs numériques
Le type de valeur "num" comprend les entiers, les réels.
Notations : Entiers: 0;41;-12
Réels : 5.0;5.412;-143.51
Opérations : + (addition); - (soustraction); * (multiplication)
/ (division); ** (exponentiel; div (division entière); mod(reste)
Les deux dernières ne sont définies que sur les entiers.

Remarque:
Certains langages distinguent entiers et réels. Parfois, les symbôles opératoires sont distincts (exemple:CAML)

Exemple de session:
? 3-7-2
-6
? 3*7+4.1
25.1
? carré (3*4)
144
? carré 3*4
36
? 3**4*2
162

2) Règles de priorités
Dans l'ordre on a: **;*;/;div;mod
+;-
Les fonctions ont des priorités plus fortes que les opérations
On calcule d'abord les () internes

3) Les règles d'association
Elles permettent de résoudre le problème lié à l'apparition d'opérations de même niveau.
Lorsqu'on a des opérations de même niveau, on compose de gauche à droite.
Les deux exceptions sont ** et ->. Dans ces deux cas, l'opération est appliquée de gauche à droite.
Exemple:
. 7-15+2 signifie (7-15)+2 et non 7-(15+2)
. 3**2**2 signifie 3**(2**5) et non (3**2)**5
. alpha -> beta -> gamma signifie alpha -> (beta -> gamma) et non (alpha -> beta) -> gamma
. f x y signifie (fx)y et non f(xy)
. 7 mod 3 vaut 1
. (-7) mod 3 vaut 2
. 7 div 3 vaut 2
. (-7) div 3 vaut -3

4) Exemple d'utilisation des oopérations de base
cf TD

III - Les valeurs de vérité (bool)
1) Valeurs booléennes
Elles servent à comparer des expressions. Notées bool, elles se composent de deux valeurs VRAI et FAUX. Toute fonction renvoyant un booléen est dit prédicat.

2) Les opérations de comparaison
On y trouve : = ; /= ; < ; > ; >= ; <=
Session :
? 2 = 2
Vrai
? 5 <2+1
Faux
? Vrai = Faux
Faux
? Faux < Vrai
Vrai

Règle : les expressions que l'on peut analyser sont de même type et ont des représentations canoniques.

3 - Les opérations logiques
On y trouve : ou, et, non


4) Les règles de priorité
Priorité plus forte que les opérations arithmétiques.
Priorité dégressive non, ou, et.
Au même niveau, évaluation de gauche à droite
Exemple :
? 1<2 et 7<12
Vrai
? non (1<2)
Faux

5) Exemple d'utilisation
a) Déterminer si une année est bisextile
-Spécification
Une année bisextile est une année divisible par 4 sauf si elle est divisible par 100 auquel cas elle doit être divisible par 400.
- Implémentation
bisextile x=(x mod 400=0) si x mod 100=0
      =(x mod 4)=0 sinon

ou bisextile x=(x mod 4=0) et (x mod 100=0) ou (x mod 400=0)

IV - Les caractères et chaînes (car et chain)
1) Les caractères
Les valeurs du type "car" se compose de l'ensemble des caractères ASCII, c'est à dire les signes visibles et les caractères de contrôle.
On les dénote en les mettant entre ''. 'a','A','5','-'...

Fonctions primitives: 2 fonctions permettent la conversion caractère en ASCII et inversement:
code: car -> num
décode: num -> car

Exemple:
?code 'b'
98
?decode 98
'b'
?decode (code 'b' ->1)
'c'
?code '7
' 10

Fonction de comparaison : 2 caractères peuvent être comparés. Elle est faite en comparant leur code ASCII.
Exemple:
?'a' < 'b'
Vrai
?'A' < 'a'
Vrai

Des fonctions simples manipulant des caractères
. Chiffre : car -> bool
chiffre x='0' <= x et x <='9'
. Majuscule: car -> bool
majuscule x='A' <= x et x<='2'
. Minuscule: car -> bool
minuscule x = 'a' <=x et x<='z'
. conv_min_maj: car -> car
conv_min_maj x = decode (ecart+code x) si minuscule sinon
        = x, sachant que ecart = code 'A' - code 'a'

Traduction en CAML:
En CAML, il est en général nécessaire de prendre le code ASCII afin de comparer.
Le code ASCII est obtenu avec la fonction int_of_char
Le caractère associé est obtenu avec la fonction char_of_int

Exemple:
let chiffre x=(int_of_char '0' <= int_of_char x) & (int_of_char x <= int_of_char '9');;
let majuscule x=(int_of_char 'A') <= int_of_char x) & (int_of_char x <= int_of_char 'Z');;
let min-maj x=let ecart=int_of_char 'A'.int_of_char 'x'
          in
          if minuscule x
              then char_of_int (ecart int_of_char x)
              else x;;

2) Les chaînes
Elles sont des suites de caractères. Le type "chaîne" comprend toutes les chaînes. On les dénote en les mettant entre guillemets.
"Bonjour" "1231.167" "x-y215"

Fonction de comparaison
Elles s'effectuent dans l'ordre lexicographique
Exemple:
?"Bonjour" < "Bonjour"
Faux
?"Bon" < "Bonjour"
Vrai
?"Bonjour"
"Bonjour"


3) L'opération de concaténation notée ^
Elle renvoie comme chaîne résultat, la suite de caractères obtenus en mettant les deux chaînes à la suite l'une de l'autre.
Exemple:
a = "Bon"
b = "jour"
c = a'b
?c
"Bonjour"

Fonction "long_chaîne" donNant la longueur d'une chaîne
Exemple:
?long_chaîne "Bonjour"
7

4) Fonction manipulant des chaînes
La justification de l'affichage:


On suppose connue une fonction espace n qui renvoie une chaine de n espaces, on obtient alors :
justifierg n x = x^espace (n-m) si n >= m
sachant que m = long_chaine x
justifierd n x = espace(n-m)^x si n>=m
sachant que m = long_chaine x
justifierc n x = espace gm^x^espace dm si n>=m
sachant que dm=(n-m)-gm
sachant que gm = (n-m) div 2
sachant que m=long-chaine x

V) Les n-uplets
1) Définition: On peut former des nouveaux types en combinant des types par paires, par n-uplets...
Mathématiquement, il s'agit d'élément appartenant au produit cartésien de plusieurs ensembles.
E1*E2*...*En = {(x1...xn}, pour tout i appartenant à [1,n] xi appartient à Ei}
Produit cartésien de plusieurs types:
Soient n ensemble E1,...,En tel que ti est le type associé à l'ensemble Ei
Alors t1*t2*...*tn est le type associé à E1*E2*...*En
Les valeurs du produit sont notées (v1,...,vr) avec vi de type ti et sont des n-uplets.
vi appartient [1,n], vi:ti => (v1,...,vn):t1*t2*...*tn
Exemple: num * car est le type des paires dont le premier élément est un nombre, le deuxième étant un caractère.
num*bool*chaîne est le type des triplet...

Session:
? (4+2,'a')
(6,'a')
? (5,Vrai,'Bonjour')
(5,Vrai,'Bonjour')
Remarque: On peut introduire des définitions locales dans les n-uplets.
t=(x*x,3*x)
sachant que x=5
?t
(10,15)

2)Exemple d'utilisation
a)
quot-reste: num*num -> num*num
quot-reste (x,y) = (x div y,x mod y)
?quot-reste (7,2)
(3,1)
?quot-reste (7.1,2.2)
T inversé

b) Racines d'une équation de second degré
rac-equat (a,b,c)=(c1,c2) si (a /=0) et (d>=0)
   sachant que r1=(-b+r)/(2*a)
      r2=(-b-r)/(2*a)
          sachant que r = racine d
              sachant que d= b*b-4*a*c

Remarques :
i) L'operateur "div" n'est pas définit en CAML
ii) La fonction "rac-equat" n'est pas complètement définit => erreur si a=0 ou d<0

3) Comparaison des n-uplets (=,<=,>=,<,>)
Elle se fait dans l'ordre lexicographique en prenant les composants de gauche à droite.
Session:
? (3,4) = (4,3)
Faux
? ((1,2),3) = (1,(2,3))
Faux
? ('a','b') = ('a','b')
Vrai
? (3,6) < (4,2)
Vrai
Seule l'= est définit en CAML.

VI - Les filtres
La notion de filtre peut être vue comme une généralisation de la notion de paramètre.

Définition: . Un filtre décrit avec précision les différentes formes que peut prendre un paramètre et indique la façon de calculer le résultat.
     Un filtre est applicable à des arguments de n'importe quel type.

Constructon: Un filtre est une expression formée à l'aide des constructions de valeurs, de constantes, de paramètres formels ayant des noms tous positifs et éventuellement le symbole -.

Quelques exemples de fonctions définies par filtrage
1- Condition Vrai x y = x
Condition Faux x y = y
2- Non Vrai = Faux
Non Faux = Vrai
3- Fact 0 = 1
Fact n=n*fact (n-1)
4- un_argument (Vrai,x,-) = x
un_argument (Faux,-,y) = y
Règles :
i) Quand une valeur est filtrée par plusieurs filtres, c'est la première qui est retenu.
ii) Le filtrage d'un paramètre formel effectue la liaison de la valeur au nom du parametre formel
iii) Le _ filtre toute valeur sans créer de liaison.

Le filtrage en CAML:
1- Match expression with
Filtre_1 -> result_1
|.......
|Filtre_n -> result_n

let condition P x y =
        match P with
        true -> x
        |false -> y;;
Remarque: On ne définit pas explicitement de fonctions.

2- Let nom_funct = function
      filtre1 -> expr1
      |....
      |filtren -> exprn;;
let rec fact = function
      0 -> 1
      |n -> n*fact (n-1);;
let reponse = function
      "oui" -> true
      |"non" -> false;;

Chp 4 - Les listes
I - Introduction
On va définir le concept de liste, présenter les principales...

II - Le type liste
1- Définition-Notation
. Une liste est un ensemble de valeurs d'un même type linéairement ordonnée.
. Une relation d'ordre est définit à partir du rang des éléments de la liste (le rang du premier = 0)
. La liste de valeur v1,...,vn est notée [v0,v1,...,vi,...,vn-1]
Exemple:
[1;2;3] liste de trois nombres
[[]] liste d'une liste
[] liste vide
[[1;2],...,[3;5]] liste de listes à deux éléments
si alpha est le type des éléments, la liste est [alpha]
Exemple:
[1;2;3]:[Num]
[[1;2];[2;3]]:[[Num]]
Remarque: Etant donné que les chaînes sont des listes de carctères, toutes les opérations sur leslistes sont applicables aux chaînes.
Deux listes sont égales si et seulement si elles contiennent les même éléments dans le même ordre.

Session:
?[1;1] = [1]
Faux
?[1;2;4]=[3;2;1]
Faux
?[1;9;11]=[1;9;11]
Vrai

Cas particuliers: [a...b] = {la liste des entiers de a à b si a<=b
{[] si a>b

2- Définition d'une liste en compréhension
La définition posée précédemment est extensible, on énumère la liste.
Elle ne donne pas un moyen pour définir une liste donnée en comprehension
l=[x|homme x,age(x) appartient à [20;40]]
Elle se déclare : [< expression >|< qualificateur >...< qualificateur >]
où < expression > dénote : -une expression booléenne
            -un générateur

< variable> <- < liste >
[< variable >,< variable >] <- < liste de paires >
Le premier signifie que la variable reçoit toutes les valeurs de la liste.
Le second signifie la conjonction des deux proprités. Les derniers qualificateurs varient plus vite que leurs prédécesseurs.

Session:
?[x*x|x <- [1..10], pair x]
[4;16;36;64;100]
?[(a,b)|a<-[1..3],b<-[1;2]
[(1,1);(1,2);(2,1);(2,2);(3,1);(3,2)]
?[(i,j)|i <- [1..4], pair i,j <- [i+1..4],impair j]
[(2,3)]

Remarque: Des listes définies en compréhension peuvent être utilisées dans les définitions.
Exemple:
. diviseur n = [d|d <- [1..n],n mod d=0]
liste des diviseurs de n (entier n>=1)
. Premier a = [diviseur n=[1..n]]
tester si n>=1 est premier ou non

Remarque:
Les définitions de listes en compréhension n'existent pas en CAML.

III - Opérations et fonctions de base sur les listes
1- La concaténation des listes
Etant données deux listes de même types
l1 = [e1,...en] et l2=[f1,...,fn]
@ renvoie la liste:
l1@l2 = [e1,...,en,f1,...,fn]

Session:
?[1;2;3]@[4;5]
[1;2;3;4;5]
type:[alpha] -> [alpha] -> [alpha]

Convention: x,y,z... désignent des éléments de listes.
xs,ys,zs...désignent des listes
xss,yss,zss... désignent des listes de sites.

2- La fonction concat
concat: [[alpha]] -> [alpha]
concat xss = [x|xs<-xss,x<-xs]

Session:
?concat [[1;2];[];[3;2;1]]
[1;2;3;2;1]

Remarque: concat n'est pas prédéfinit en CAML

3- La longueur d'une liste
long_liste:[alpha] -> num
Exemple:
?long_liste []
0
?long_liste [1;1;2;2;3;3]
6

Remarques:
. long_liste (xs@ys)=long_liste xs + long_liste ys
. long_liste (expression|x<-xs)=long_liste xs

4- L'opérateur :: (cons)
x est un élément de type alpha et xs un eliste de type [alpha]
:: renvoie comme liste celle obtenue en insérant l'élément x en tête de xs.
Si xs=[x1;...;xn] alors
x::xs=[x;x1;...;xn]

Session:
?1::[]
[1]

Remarque:
[x1;x2;...;xn]=x1::x2::...::xn::[]

5- Tête et queue de liste
"tête" renvoie le premier élément d'une liste.
"queue" renvoie la liste privée de son premier élément.
tête:[alpha]->alpha
queue:[alpha]->[alpha]

Propriétés:
tête ([x]@xs)=x=tête(x::xs)
queue([x]@xs)=xs=queue(x::xs)
tête[]=T inversé
queue[]=T inversé
xs=[tête xs]@queue xs

6-Filtrage sur leslistes
Les filtres sont soit:
- la constante []
- bâtis avec ::

Remarque: Car une liste est obtenue d'une et d'une seule avec [] et ::
Exemple:
1- singleton [] = Faux
singleton [x] = Vrai
singleton x::y::xs=Faux
Foncton booléenne testant si une liste est un sigleton.

2- vide [] = Vrai
vide _ = Faux
3- tête x::xs=x
tête [] = T inversé

IV - Fonction de manipulation de listes
1 - Les fonctions "retirer" et "prendre"
n>0 et une liste xs
retirer n xs=retire les n premiers éléments de xs
prendre n xs=renvoie les n premiers éléments de xs

Exemple:
?Prendre 3 [1..10]
[1;2;3]
?retirer 3 [1..6]
[4;5;6]

2- Les fonctions "retire tq" et "prendre tq"
Elles agissent comme les précédentes à ceci près que leur premier argument est un prédicat à laplace d'un entier.

prendretq p xs réunie le plus segment initial de xs dont les éléments satisfont p.
retiretq p xs supprime dans xs le plus grand segment initial dont les éléments satisfont p.

?prendretq pair [2,4,6,1,5,6]
[2,4,6]
?retiretq (='a') ['a','a','b','0']
['b','0']

3- La fonction "renverser"
Elle renvoie la liste en ordre inverse.
?renverser ["Bonjour";"a;"tous"]
["tous";"a";"Bonjour"]

4- Les fonctions "coupler"
Elle a pour argument une paire de liste, elle renvoie une liste de paires.
Si elles n'ont pas lamême longueur, le couplage s'arrête sur la liste la plus courte.
?coupler([1..3],[5..7])
[(1,5);(2,6);(3,7)]
?coupler ([1..5] ["B","a","n"])
[(1,B);(2,0);(3;n)]

i) Produit scalaire: X.Y=somme (de i=1 à n) de xi*yi
ps (xs,ys)=somme[x*y|(x,y)<-coupler(xs,ys)] où l'on suppose connue la fonction "somme" qui ajoute les éléments d'une liste.

ii) Position de la première occurence de x dans xs
a- position xs x = [i|(i,y) <- coupler ([0..long_liste xs],xs),x=y]
b- position xs x = tete((positions xs x)@[-1])

5-Les fonctions "appliquer" et "filtrer"
La fonction "appliquer" applique une fonction f donnée à tous les éléments d'une liste, et renvoie la liste composée des résultats trouvés.

appliquer: (alpha -> beta) -> [alpha] -> [beta]
appliquer f xs = [f x|x <- xs]

Session:
?appliquer carré [9;2]
[81;4]
?somme (appliquer carré [1..5])
55

Propriété:
appliquer f(xs@ys) = (appliquer f xs)@(appliquer f ys)
"filtrer" a pour argument un prédicat p et un eliste xs. Elle renvoie la sous-liste des éléments de xs vérifiant p.
filtrer:(alpha->bool)->[alpha]->[alpha]
filtrer p xs:[x|x <-xs,p x]

Session:
?filtrer pair [1;2;4;5;62]
[2;4;62]
?appliquer carré filtrer pair [1..10]
[4;16;36;64;100]

Propriétés: filtrer p (xs@ys)=(filtrer p xs)@(filtrer p ys)

6- Traduction des listes en compréhension
Toute liste définie en compréhension commençant par un générateur peut-être traduite à l'aide des fonctions "appliquer", "filtrer", "concat".
Pratiquement, on y parvient à l'aide des quatre règles suivantes:
i) [x|x<-xs]=xs
ii) [f x|x <- xs]=appliquer f xs
iii) [e|x <- xs,px,...]=[e|x <- filtrer pxs...]
iv) [e|x <- xs,y <- ys...]=concat[e|y<-ys]x<-xs

Exemple:
[x*x|x<-xs,pair x]=appliquer carré (filtrer pair xs)

V - Les opérateurs "recomposer"
Ils vont permettre de convertir des listes en autre chose que des listes.
Il y a deux schemas possible que l'on va maintenant presenter.

1) L'operateur "recomposerg"
recomposerg : (beta -> alpha -> beta) -> beta -> [alpha] -> beta
(*** vient ensuite une feuille simple sur recomposerd et recomposerg. Je ne l'ai pas retranscrit car les indices y pullulent***)

Chp 5 - La récursivité

I - Introduction
On va mettre l'accent sur les deux notion fondamentales que sont :
- les définitions récursives
- les principes de récurrence
En programmation fonctionnelle, elles nous permettent de:
-définir des fonctions introduites précédemment
-de démontrer un certain nombre de propriétés des opérateurs en fonction.

II - Récursivité et récursion
1- Définitions récursives
Une définition est récursive si elle fait appel à elle même sans sa définition où si elle fait appel à d'autres définitions qui feront appel au moins une fois à sa définition.

Exemple: Calcul de x^n sachant que x appartient à N+
x**n=1 si n=0
  =x*x**(n-1), si n>0

En utilisant des filtres, on peut écrire:
x**0=1 (1)
x**n=x*x**(n-1) (2)

La définition de x^n est bien récursive: pour une valeur de x, la valeur de x^n est obtenue à partir de la valeur de x^n-1 dont la valeur est obtenue à partir de x^n-2 et ainsi de suite jusqu'à ce que l'on atteigne x^0 connu.

2- Principe de récurrence sur les entiers
Soit P(n) vraie, une propriété qui dépend d'un entier n>=0.
Pour tout n appartenant à N, P(n) vraie <=> 1. P(0) vraie
              2. Pour tout n appartenant à N*, si P(n-1) vraie alors P(n) vraie.

-> x**x(m+n)=(x**m)*(x**n)
avec x>0 et (m,n) appartenant à R²
Il suffit de faire un raisonnement par récurrence sur m
a/ x**x(0+n)=x**(n)       *(Propriété de l'+)*
      =1*x**(n)       *(Propriété de l'*)*
      =(x**0)*(x**(n))       *(Propriété n°1)*

b/ Supposons la propriété vraie pour la valeur m-1
x**(n+m)=x**((m-1+n)+1)       *(Propriété de l'+)*
    =x*(x**(m-1+n))       *(Propriété n°2)*
    =x*((x**(m-1))*(x**n))       *(Hypothèse de récurrence)*
    =x*(x**(m-1)*x(x**(n)))       *(Propriété de l'*)*
    =(x**m)*(x**n)

III - Récursion et récurrence des listes
. Pour les entiers, la récurs° et la récurrence sont basées sur deux cas: 0 et n
. Ces principes peuvent être appliqués aux listes
. Elles vont reposer sur deux cas: liste [] et liste x::xs pour x et xs.

1-Définitions récursives sur des listes
Donnons quelques exemples de définitions récursives poratnt sur des listes:

i) Fonction long_liste
long_liste []=0
long_liste (x::xs)=1+long_liste xs

ii) L'opération concaténation des listes
[]@ys=x::(xs@ys)

iii) Inversion d'une liste
renverser [] = []
renverser [x::xs]=renverser [xs]@[x]

2- Principes de récurrence sur les listes finies
Soit P(xs) une propriété qui dépend d'une liste finie xs: [alpha]
Pour tout xs finie, P(xs) vraie <=> |i) 1-P([]) vraie
          |ii) Pour tout x:alpha, si P(xs) vraie alors P(x::xs) vraie
Remarque: On peut formaliser ce principe à partir du principe de récurrence sur les entiers en utilisant la longueur des listes.
Exemple: Preuve de l'associativité de @
-> xs@(ys@zs)=(xs@ys)@zs
Preuve par récurrence sur xs
. Cas []
[]@(ys@zs)=(ys@zs)
      =(([]@ys)@zs)
. Cas (x::xs) supposé vrai pour xs
(x::xs)@(ys@zs)=x::(xs@(ys@zs))
        =x::((xs@ys)@zs)
        =(x::(xs@ys))@zs
        =((x::xs)@ys)@zs

IV - Définition formelle d'opérateurs sur les listes
Au chapitre 4, on a vu des fonctions manipulant des listes, on va donc voir:

1- La fonction "coupler"
On la définit par filtrage (liste exhaustive)
coupler ([],ys)=[]
coupler (x::xs,[])=[]
coupler (x::xs,y::ys)=(x,y)::(xs,ys)

Remarque: On peut vérifier que les filtres recouvrent bien toutes les combinaisons possibles des deux arguments.

2-Preuve par induction sur les filtres
Le principe de récurrence sur les listes peut-être généralisé avec les définitions récursives définies par filtrage, ceci dans la mesure où l'on a définit la liste exhaustive des filtres correspondants à toutes les combinaisons des arguments impliqués.

Exemple: montrer que:
lg_liste coupler (xs,ys)=min (lg_liste xs) (lg_liste ys)
. Cas ([],ys):
lg_liste coupler ([],ys)=lg_liste[]
                                 =0
                                 =min 0 lg_liste ys
                                 =min lg_liste [] lg_liste ys

. Cas (x::xs,[])
lg_liste coupler (x::xs,[])=lg_liste []
                                    =0
                                    =min 0 lg_liste (x::xs)
                                    =min lg_liste (x::xs) lg_liste[]

3-Les fonctions prendre et retirer
prendre 0 xs = []
prendre n [] = [}
prendre n (x::xs) = x::prendre (n-1) xs

retirer 0 xs = xs
retirer n []
retirer n (x::xs) = retirer (n-1) xs

Remarques :
1/ Une preuve/recurrence sur n, ne prevoit pas la possibilite d'avoir n=T inverse; si on veut le prevoir, il faut l'inclure dans la demonstration comme cas additionnel.
2/ Il en est de même avec une variable liste qui peut recevoir la valeur T inverse, il faut la traiter separement.

Preuve par filtrage de la propriete :
(prendre n xs)@(retirer n xs) = xs

- Cas 0 xs :
(prendre 0 xs)@(retirer 0 xs)
                        = []@[] (1 de prendre et retirer)
                        =xs (propriete 1 de @)

- Cas n [] :
(prendre n [])@(retirer n [])
                     = []@[] (2 de prendre et retirer)
                     = [] (1 de @)

- Cas n (x::xs) : Vrai pr ((n-1)xs)
(prendre n (x::xs))@(retirer n (x::xs))
                           =x::((prendre (n-1) xs)@(retirer (n-1) xs))
                           =(x::xs)

Remarques :
prendre 0 Tinverse = []
prendre Tinverse [] = Tinverse
retirer 0 Tinverse = []
retirer Tinverse [] = Tinverse

=> prendre Tinverse [] @ retirer Tinverse [] = Tinverse @Tinverse = Tinverse
ne verifie pas la propriete.

4-Les fonctions tete et queue
tete (x::xs) = x
queue (x::xs) = xs

tete [] = Tinverse
et queue [] = Tinverse
Propriete : [tete xs]@[queue xs] = xs quelque soit xs /= []
La demonstration se fait par analyse des cas sur xs.
[tete (x::xs)]@[queue(x::xs)] = [x]@xs = (x::xs)

5-Les fonctions appliquer et filtrer
appliquer f [] = []
appliquer f (x::xs) = f x::(appliquer f xs)

filtrer p [] = []
filtrer p (x::xs) = x::(filtrer p xs) si px
                     = filtrer p xs sinon

On peut montrer que :
filtrer p (appliquer f xs) = appliquer f (filtrer (p o f) xs)

VI - Auxiliaires et generalisations
Dans certains cas, pour definir une fonction ou pour prouver un theoreme il est plus facile ou necessaire :
- Soit de definir des fonctions auxiliaires ou de prouver des theoremes auxiliaires
- Soit de definir des definitions plus generales ou de provoquer des theoremes plus generaux.

Un exemple : la fonction renverser possede la propriete suivante :
renverser (renverser xs) = xs

[tete (x::xs)]@[queue (x::xs)] = [x]@xs = (x::xs)
La demonstration est plus facile si l'on procede comme suit :
i/ Montrer par induction que renverser (ys@[x]) = x::renverser ys
ii/ L'appliquer ensuite a la propriete a demontrer.

Montrons le resultat auxiliaire :
renverser (ys@[x]) = x::renverser ys
raisonnement par recurrence sur ys.

. Cas [] :
renverser ([]@[x]) = [x] = x::renverser []

. Cas (y::ys) : vrai pour ys
renverser (y::ys@[x])
=renverser (ys@[x])@[y]
=x::renverser ys@[y]
=x::renverser (y::ys)
On peut alors montrer que renverser (renverser xs) = xs
raisonnement par recurrence sur xs.

. Cas [] :
renverser (renverser []) = renverser [] = []

. Cas (x::xs) : vraie pour xs
renverser (renverser (x::xs))
=renverser (renverser xs@[x])
=x::renverser (renverser xs)
=x::xs

VII - Implementation des resultats en CAML
let rec renverser = function [] -> []
|x::xs -> renverser xs@[x];;

let rec coupler = function
([],ys) -> []
|(x::xs,[]) -> []
|(x::xs,y::ys) -> (x,y)::coupler (xs,yx);;

let rec prendre = function
0, xs -> []
|n,[] -> []
|n,x::xs -> x::prendre (n-1,xs);;

let rec retirer = function
let appliquer = map;;
let recomposerg = it_list;;
let recomposerd = list_it;;
let tete = hd;;
let queue = tail;;
let long_liste = list_lenght;;

let rec filtrer = function
p,[] -> []
|p,x::xs -> if px
then x::(filtrer(p,xs))
else filtrer (p,xs);;

let rec intervalle a b = if a>b
then []
else a::intervalle (a+1) b;;

Hosted by www.Geocities.ws

1