« INFO421 : Programmation fonctionnelle » : différence entre les versions

De Wiki du LAMA (UMR 5127)
Aller à la navigation Aller à la recherche
 
(119 versions intermédiaires par 6 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
Ce wiki est un complément de cours pour le cours « info-401 : programmation fonctionnelle ». La participation au wiki est fortement encouragée, et deviendra peut-être obligatoire...
Ce wiki est un complément de cours pour le cours « info-421 : programmation fonctionnelle ». La participation au wiki est fortement encouragée, et deviendra peut-être obligatoire...


Pour pouvoir modifier les pages, inscrivez-vous (lien en haut à droite) pour obtenir un login et mot de passe. (Utilisez votre vrai nom...)
Pour pouvoir modifier les pages, inscrivez-vous (lien en haut à droite) pour obtenir un login et mot de passe. (Utilisez votre vrai nom...)
Ligne 12 : Ligne 12 :




== Détails techniques ==


=== Compléments de cours, TD et TP ===
=Détails techniques=


==== Installer Ocaml ====
==Nouvelles==


Si vous voulez installer OCaml sur votre ordinateur :
Les nouvelles récentes sont en haut de la liste...

* [[Utilisateur:Hyvernat|Hyvernat]] 12 janvier 2009 à 09:59 (CET) : création du wiki.
* [[Utilisateur:Hyvernat|Hyvernat]] 22 janvier 2009 à 19:05 (CET) : j'ai rajouté une page [[INFO401_corr_TD_TP | correction des TD / TP]].
* [[Utilisateur:Hyvernat|Hyvernat]] 22 janvier 2009 à 19:05 (CET) : la DSI a modifié des trucs qui devraient supprimer certains des problèmes qu'on a eu en TP. Contactez-moi si vous rencontrez des problèmes en utilisant Emacs et Caml...

==Organisation des séances==

* séance 1 (12/01/2009). Cours : introduction, programmation fonctionnelle, le langage Caml.
* séance 2 et 3 (19/01/2009).
*# Cours : les valeurs, les types atomiques et fonctionnels (section [[#Les_fonctions | Les fonctions]]).
*# TD1
* séance 4 (21/01/2009) : TP0 : prise en main de Caml.

==Compléments de cours, TD et TP==


<big>
<center>'''<u>[[INFO401 corr TD TP | Corrections (partielles) des TD / TP]]</u>'''</center>
</big>

===Les travaux dirigés===

* feuille de TD1 : [http://www.lama.univ-savoie.fr/~hyvernat/Enseignement/0809/info401/td1.pdf premières expressions en Caml]

===Les travaux pratiques===

====Ocaml====

Si vous voulez installer OCaml sur votre ordinateur.


* Sous Linux : c'est la solution idéale. Il existe probablement des paquets pour votre distribution. Pour Ubuntu, pour avoir un environnement similaire à ce que vous aurez dans les salles informatiques, installez les paquets <tt>ocaml</tt>, <tt>ocaml-core</tt>, <tt>ocaml-mode</tt>, <tt>tuareg-mode</tt> et <tt>emacs</tt>.
* Sous Linux : c'est la solution idéale. Il existe probablement des paquets pour votre distribution. Pour Ubuntu, pour avoir un environnement similaire à ce que vous aurez dans les salles informatiques, installez les paquets <tt>ocaml</tt>, <tt>ocaml-core</tt>, <tt>ocaml-mode</tt>, <tt>tuareg-mode</tt> et <tt>emacs</tt>.
* Sous MacOS : il semblerait qu'il y ait des paquets MacPorts pour <tt>ocaml</tt>, <tt>emacs</tt> et <tt>tuareg-mode.el</tt>.

* Sous Windows : je vous renvoie au tutoriel de Jean-Paul Roy : [http://deptinfo.unice.fr/~roy/CAML/Win/install-win.html Installation de OCaml (sur Windows XP)]. Je n'ai malheureusement (??) pas accès à une machine avec Windows (98/2000/XP/Vista/7), je ne pourrais donc pas beaucoup vous aider.
* Sous Windows : je vous renvoie au tutoriel de Jean-Paul Roy : [http://deptinfo.unice.fr/~roy/CAML/Win/install-win.html Installation de OCaml (sur Windows XP)]. Je n'ai malheureusement (??) pas accès à une machine avec Windows (98/2000/XP/Vista/7), je ne pourrais donc pas beaucoup vous aider.


Contactez moi si vous avez des problèmes.
Contactez moi si vous avez des problèmes.


Pour les exemples simples, vous pouvez aussi utiliser [http://try.ocamlpro.com/ OCaml en ligne]
J'ai créé une petite [http://www.lama.univ-savoie.fr/wiki/index.php?title=INFO401_:_utilisation_Caml page dédiée pour la syntaxe de Caml et son utilisation].


==== Références supplémentaires ====


* Le livre d'Emmanuel Chailloux, Pascal Manoury et Bruno Pagano : [http://www.pps.jussieu.fr/Livres/ora/DA-OCAML/ Développement d'applications avec Ocaml]. (Ce livre utilise une vieille version de Ocaml, mais reste pertinent.)
====TP0 : prise en main de Caml====
* La documentation de Caml, version 3.09 (utilisé en TP) : [http://caml.inria.fr/pub/docs/manual-ocaml-309/ Documentation and user's manual].
* Le livre de Jason Hickey [http://files.metaprl.org/doc/ocaml-book.pdf Introduction to Objective Caml] (en anglais).


==== Cours ====
Le TP0 [http://www.lama.univ-savoie.fr/~hyvernat/Enseignement/0809/info401/tp0.pdf prise en main de Caml], au format pdf.


==== TD et TP ====
* le petit fichier pour lancer l'interprète Caml : [http://www.lama.univ-savoie.fr/~hyvernat/Enseignement/0809/info401/interprete-caml.desktop interprete-caml]. <small>(Pour le sauvegarder : clique droit sur le lien, "<tt>Enregistrer la cible du lien sous...</tt>" ; mettez le sur le bureau, et ne modifiez pas le nom du fichier...)</small>


* [http://www.lama.univ-savoie.fr/~hyvernat/Enseignement/1213/info421/td1.pdf TD1 : premières expressions en Caml (pdf)]
* [http://caml.inria.fr/pub/docs/manual-ocaml-309/libref/Graphics.html la documentation de la librairie <tt>graphics</tt>].


== Introduction ==

===Références supplémentaires===

* Le livre d'Emmanuel Chailloux, Pascal Manoury et Bruno Pagano : [http://www.pps.jussieu.fr/Livres/ora/DA-OCAML/ Développement d'applications avec Ocaml]. (Ce livre utilise une vieille version de Ocaml, mais reste pertinent.)

* La documentation de Caml, version 3.09 (utilisé en TP) : [http://caml.inria.fr/pub/docs/manual-ocaml-309/ Documentation and user's manual].

=Introduction=


Pour paraphraser un collègue dont je ne retrouve pas le nom :
Pour paraphraser un collègue dont je ne retrouve pas le nom :


<blockquote>
<center>''Attention :'' il ne s'agit pas d'un cours de programmation fonctionelle</center>
''Attention :'' il ne s'agit pas d'un cours de programmation fonctionelle
</blockquote>


Il s'agit plutôt d'un cours de programmation '''fonctio<u>nn</u>elle'''...
Il s'agit plutôt d'un cours de programmation '''fonctio<u>nn</u>elle'''...


=== Petit historique censuré ===

==Petit historique censuré==


Je ne parlerais pas des langages pré-historiques (cartes perforées, λ-calcul, machines de Turing, ...)
Je ne parlerais pas des langages pré-historiques (cartes perforées, λ-calcul, machines de Turing, ...)
Ligne 97 : Ligne 66 :
* Smalltalk (fin des année 1983), début de la programmation objet.
* Smalltalk (fin des année 1983), début de la programmation objet.
* 1983 : ADA.
* 1983 : ADA.
* année '80 : Caml (1987), puis CamlLight(1990), puis OCaml(1996), développé
* 1983 : Miranda un langage fonctionnel « pur ».
à l'INRIA. (Dernière version en octobre 2012.)
* 1985 : Caml, développé à l'INRIA.
* années '90 : Python (version 1.0 en 1994).
* 1990 : Haskell, inspiré de Miranda.
* années '90 : PHP (version 1.0 en 1995).
* années '90 : Java (version 1.0 en 1996).


Je vous conseille d'aller voir le graphique suivant : [http://www.levenez.com/lang/lang.pdf Computer Languages Timeline] (ou [http://www.levenez.com/lang/redirect_lang_a4_pdf.html découpé en pages A4]).
Je vous conseille d'aller voir le graphique suivant : [http://www.levenez.com/lang/lang.pdf Computer Languages Timeline] (ou [http://www.levenez.com/lang/redirect_lang_a4_pdf.html découpé en pages A4]).


==Fonctionnel ??==
=== Fonctionnel ?? ===


L'adjectif ''fonctionnel'' a au moins deux sens :
L'adjectif ''fonctionnel'' a au moins deux sens :

# qui fonctionne, en état de marche,
# qui fonctionne, en état de marche,
# qui se rapporte aux fonctions.
# qui se rapporte aux fonctions.


Les langages fonctionnels sont bien entendus ''fonctionnels'' dans le premier sens, mais c'est surtout le second sens qui nous intéresse. Les langages tels que Pascal, Ada ou C sont qualifié, par opposition, d'''impératifs''.
Les langages fonctionnels sont bien entendus ''fonctionnels'' dans le premier sens, mais c'est surtout le second sens qui nous intéresse. Les langages tels que Pascal, Ada ou C sont qualifié, par opposition, d''''impératifs'''''.


Un des slogans de la programmation fonctionnelle en général est


<blockquote>
''<u>Les fonctions sont des valeurs comme les autres</u>''
</blockquote>


et c'est de là que vient la terminologie... En particulier, il n'y a pas de différence entre les ''instructions'' (qui ont un effet) et les ''expressions'' (qui ont une valeur). Par exemple, en Python,
Un des slogans de la programmation fonctionnelle en général est
<center>''<u>Les fonctions sont des valeurs comme les autres</u>''</center>
et c'est de là que vient la terminologie...


<pre>
Comme nous le verront, cela a de nombreuses conséquences sur l'expressivité du langage et la manière de programmer.
x = x+1
</pre>


ou
==Le langage (O)Caml==

<pre>
if delta < 0:
print("Il n'y a pas de solution")
</pre>

sont des ''instructions'' : on ne peut pas les mettre dans une variable.

Comme nous le verront, cela a des conséquences sur l'expressivité du langage et la manière de programmer.

=== Le langage (O)Caml ===


Le langage OCaml est développé par l'INRIA (Institut national de recherche en informatique et automatique). C'est un successeur de CamlLight.
Le langage OCaml est développé par l'INRIA (Institut national de recherche en informatique et automatique). C'est un successeur de CamlLight.


Le nom Caml est formé des initiales de "Categorical Abstract Machine Language", et le langage lui même appartient à la famille de ML ("Meta Language"). C'est un langage fonctionnel ''strict'' (nous verrons ce que cela veut dire), typé (nous verrons ce que cela veut dire) qui supporte plusieurs styles de programmation :
Le nom Caml est formé des initiales de "Categorical Abstract Machine Language", et le langage lui même appartient à la famille de ML ("Meta Language"). C'est un langage fonctionnel ''strict'' (nous verrons ce que cela veut dire), statiquement typé (nous verrons ce que cela veut dire) qui supporte plusieurs styles de programmation :

* fonctionnel bien sûr,
* fonctionnel bien sûr,
* mais aussi impératif,
* mais aussi impératif,
* objet également (c'est le « O » de OCaml).
* objet également (c'est le « O » de OCaml).


Dans ce cours, nous utiliserons seulement le style fonctionnel.
Dans ce cours, nous utiliserons principalement le style fonctionnel, et un peu le style impératif en fin de semestre.


Voici quelques aspects importants du langages que nous verrons peut-être pendant le cours :
Voici quelques aspects importants du langages que nous essayeront d'aborder pendant le cours :

* fonctions comme valeurs valeurs,
* fonctions comme valeurs,
* types de données ''algébriques''
* polymorphisme,
* polymorphisme,
* système d'exceptions,
* système d'exceptions,
* système de modules et de foncteurs,
* support pour des références et des données mutables (programmation « impure »),
* support pour des références et des données mutables (programmation « impure »),
* système de modules et de foncteurs,
* ...
* ...


La notion de ''récursivité'' sera fondamentale pendant toute la durée du cours...

Un aspect intéressant du langage est que c'est :


Un aspect intéressant du langage est que c'est soit :
* un langage interprété (avec l'interpréteur OCaml),
* un langage interprété (avec l'interpréteur OCaml),
* soit un langage compilé en bytecode (code binaire indépendant de l'architecture),
* soit un langage compilé en bytecode (code binaire indépendant de l'architecture),
* soit un langage compilé optimisé en binaire (dépendant de l'architecture).
* soit un langage compilé optimisé en binaire (dépendant de l'architecture).


=== Autres langages fonctionnels ===
On peut donc choisir ce que l'on veut pour ces applications.


==Autres langages fonctionnels==
Il existe de nombreux autres langages fonctionnels. Par exemple :


Il existe de nombreux autres langages fonctionnels. Je vous montrerais peut-être des exemples en cours. Voici quelques exemples importants :
* SML (Standard ML) : [http://en.wikipedia.org/wiki/Standard_ML Wikipedia], autre dialecte de la famille ML.
* SML (Standard ML) : [http://en.wikipedia.org/wiki/Standard_ML Wikipedia], autre dialecte de la famille ML.
* LISP, dont les deux dialectes principaux sont :
* LISP, dont les deux dialectes principaux sont :
''' Common LISP : [http://en.wikipedia.org/wiki/Common_LISP Wikipedia],
** Common LISP : [http://en.wikipedia.org/wiki/Common_LISP Wikipedia],
''' Scheme : [http://en.wikipedia.org/wiki/Scheme_(programming_language) Wikipedia].
** Scheme : [http://en.wikipedia.org/wiki/Scheme_%28programming_language%29 Wikipedia].
* Haskell : [http://en.wikipedia.org/wiki/Haskell_(programming_language) Wikipedia] (inspiré en grande partie de [http://en.wikipedia.org/wiki/Miranda_(programming_language) Miranda]).
* Haskell : [http://en.wikipedia.org/wiki/Haskell_%28programming_language%29 Wikipedia] (inspiré en grande partie de [http://en.wikipedia.org/wiki/Miranda_%28programming_language%29 Miranda]).


Plusieurs langages impératifs intègrent maintenant des aspects propres des langages fonctionnels : Python, Scala, ...


==Applications concrètes==
=== Applications concrètes ===


Voici quelques exemples de logiciels développés en OCaml :
Voici quelques exemples de logiciels développés en OCaml :

* [http://ocsigen.org/ Ocsigen], un serveur web,
* [http://ocsigen.org/ Ocsigen], un serveur web,
* [http://www.cis.upenn.edu/~bcpierce/unison/ Unison], un logiciel de synchronisation de fichiers entre ordinateurs,
* [http://www.cis.upenn.edu/~bcpierce/unison/ Unison], un logiciel de synchronisation de fichiers entre ordinateurs,
* [http://mldonkey.sourceforge.net/Main_Page MLDonkey], un logiciel de Peer-to-peer multiréseaux,
* [http://mldonkey.sourceforge.net/Main_Page MLDonkey], un logiciel de Peer-to-peer multiréseaux,
* [http://pauillac.inria.fr/advi/ Active DVI] un visualisateur pour le format de fichier DVI.
* [http://pauillac.inria.fr/advi/ Active DVI] un visualisateur pour le format de fichier DVI,
* analyse de programmes critiques : [http://www.astree.ens.fr/ Astrée],
* informatique financiere : [http://www.janestreet.com Janestreet Capital] et [http://www.lexifi.com Lexifi].


La viabilité du paradigme fonctionnel se retrouve également dans le langage Erlang ([http://fr.wikipedia.org/wiki/Erlang_(langage) Wikipedia]), un langage fonctionnel développé par Ericsson pour la programmation concurrente de systèmes temps réels.
La viabilité du paradigme fonctionnel se retrouve également dans le langage Erlang ([http://fr.wikipedia.org/wiki/Erlang_%28langage%29 Wikipedia]), un langage fonctionnel développé par Ericsson pour la programmation concurrente de systèmes temps réels.


=== Objectifs du cours ===
D'autre part, l'efficacité des langages fonctionnels peut être constatée sur le [http://shootout.alioth.debian.org/ The Computer Language Benchmarks Game], où différents langages de programmation sont comparés (temps d'exécution, taille du code source et utilisation de la mémoire) sur des taches différentes. Les langages fonctionnels sont toujours bien représentés...

==Objectifs du cours==


# être capable de définir des fonctions récursives, et comprendre ce qu'elles font
# être capable de définir des fonctions récursives, et comprendre ce qu'elles font
# comprendre le typage et le polymorphisme à la ML
# comprendre le typage, les types algébriques et le polymorphisme à la ML
# pouvoir définir des fonction d'ordre supérieur pour modulariser votre code
# pouvoir définir des fonction d'ordre supérieur pour modulariser votre code
# être capable de décomposer un problème
# être capable de décomposer un problème
# comprendre et utiliser la notion de type récursif
# commencer à réfléchir à la complexité de vos programmes
# commencer à réfléchir à la complexité de vos programmes


=Premiers pas en Caml=
== Premiers pas en Caml ==


Un des slogans de la programmation fonctionnelle est ''tout est une valeur, ou plus précisément,
==Les valeurs==


<blockquote>
Un des slogans de la programmation fonctionnelle est ''tout est une valeur, ou plus précisément,
<center>''Tout programme <u>a</u> une valeur.''</center>
''Toute expression <u>a</u> une valeur.''
</blockquote>


Cette idée n'est pas présente en C, et encore moins en Pascal, où l'on distingue les ''expressions'' ("<tt>3*x+f(2)</tt>" par exemple) et les ''instructions'' ("<tt>if (n>22) then a:=12 else a:=13</tt>" par exemple). En Pascal, les instructions sont en général séparées par des "<tt>;</tt>" et sont ''exécutées'' en séquence. Les expressions sont juste calculées, et ne peuvent pas être séquentialisées.
Cette idée n'est pas présente en Python où l'on distingue les ''expressions'' ("<tt>3*x+f(2)</tt>" par exemple) et les ''instructions'' ("<tt>if n>22: a=12 else: a=13</tt>" par exemple). En Python, les instructions sont en général séparées par des retours à la ligne et sont ''exécutées'' en séquence. Les expressions sont juste calculées, et ne peuvent pas être séquentialisées.


Dans un langage purement fonctionnel, le "<tt>;</tt>" n'existe pas, et il n'y a que des valeurs...
Dans un langage purement fonctionnel, le "<tt>;</tt>" n'existe pas, et il n'y a que des valeurs...


Les valeurs sont formées en utilisant :
Les valeurs sont formées en utilisant :

* les fonctions du langage (addition, multiplication)
* les fonctions du langage (addition, multiplication, opérateurs logiques),
* les opérateurs logiques (qui sont juste des fonctions des booléens vers les booléens),
* des constructions du langage (<tt>if ... then ... else ...</tt> par exemple),
* les relations mathématiques (égalité, plus grand, ... qui sont juste des fonctions dont le type de retour est le type des booléens),
* les relations mathématiques (égalité, plus grand), ... qui sont juste des fonctions renvoyant des booléens,
* les constantes,
* les variables,
* les constantes du langage,
* les variables de l'environnement,
* des constructions du langage (<tt>if ... then ... else ...</tt>) par exemple).
* ''les valeurs (en particulier les fonctions) définies par le programmeur''.


Bien entendu, comme Caml est typé, il faut respecter les types.
Bien entendu, comme Caml est typé, il faut respecter les types.


=== Les types atomiques ===


Caml (et les autres langages fonctionnels typés) possèdent plusieurs types de base tels que :
===Les valeurs de type atomique===


Voici quelques exemples d'expressions bien formées : on suppose que <tt>n</tt> est de type entier, <tt>x</tt> est de type flottant et <tt>s</tt> de type chaîne de caractères.
* les booléens : type <tt>bool</tt> (valeur <tt>true</tt> et <tt>false</tt>) et les opérations <tt>not</tt>, <tt>&&</tt> (et) et <tt>||</tt> (ou).
* Les entiers : type <tt>int</tt> pour les entiers signés compris entre <math>-2<sup>30</sup></math> et <math>2<sup>30</sup>-1</math>. (Les entiers Caml sont stockés sur 31 bits...) Les opérations associés sont <tt>+</tt>, <tt>-</tt>, <tt>*</tt>, <tt>/</tt> (pour la division entière) et <tt>mod</tt> (pour le modulo).
17
* Les flottants : type <tt>float</tt> pour les réels en virgule flottante. Les opérations usuelles sont <tt>+.</tt>, <tt>-.</tt>, <tt>*.</tt> et <tt>/.</tt>.
* Les caractères : type <tt>char</tt> (notés entre guillemets simples).
* Le type unité : type <tt>unit</tt>, dont l'unique valeur est <tt>()</tt>. (Nous verrons que ce type a une utilité...)


Même s'il ne s'agit pas vraiment d'un type atomique, nous pouvons également ajouter le type des chaînes de caractères : type <tt>string</tt> (notées entre guillemets doubles).
n

n+(3*2)

(n+3)*2

n+3*2 (* --> même valeur que n+(3*2) *)

length ("Bonjour") (* --> valeur 7 *)

length "Bonjour" (* --> valeur 7 *)


<source lang="ocaml">
<source lang="ocaml">
(* exemples ... *)
if (x>0) then n
else (n+1) (* --> suivant la valeur de x, c'est soit la valeur de n, soit la valeur de n+1 *)
</source>
</source>


'''Remarques :''' les opérations booléennes ("<tt>&&</tt>" pour le « et » et "<tt>||</tt>" pour le « ou ») fonctionnent de à gauche à droite, et l'argument de droite n'est évalué que si c'est nécessaire. Par exemple <tt>true || (0 = 1/0)</tt> donne la valeur <tt>true</tt>, alors que que <tt>false || (0=1/0)</tt> lève l'exception <tt>Division_by_zero</tt>.
<source lang="ocaml">
(if (x>0) then n
else n) + 1 (* --> (presque) la même chose que n+1 *)
</source>
Voici des exemples de valeurs mal formées :
x+1 (* --> x et 1 n'ont pas le meme type *)


=== définitions et environnement ===
x+1.0 (* --> le "+" ne fonctionne que sur des entiers (Pour les flottants, il faut utiliser "+." "*.", ...) *)


Comme tous les langages de programmation, Caml garde en mémoire une liste de définitions. Chacune de ces définitions met en relation un nom ("<tt>x</tt>", "<tt>n</tt>", "<tt>length</tt>", ...) et une valeur ("<tt>3.7</tt>", "<tt>42</tt>", "<tt>fun s -> ...</tt>").
length x (* --> length a un argument de type chaine, mais x est de type flottant *)


Lorsque l'on lance Caml, l'environnement contient déjà de nombreuse fonctions prédéfinies : <tt>max</tt>, <tt>min</tt>, <tt>mod</tt>, <tt>+</tt>, <tt>float_of_int</tt>, ...
if (x=0) then x
else n (* --> le type d'une valeur doit etre connu, mais x et n n'ont pas le meme type *)


Certaines des variables de l'environnement correspondent à des paramètres de fonction en cours de définition. Ces variables n'ont pas de valeur, mais seulement un type.
if (true) then x
else n (* --> idem (meme si dans ce cas, on peut ignorer le "else") ()


==== Environnement ====


* Liste de paires <tt>nom</tt> / <tt>valeur</tt>,
Caml donne des messages d'erreur explicites dans ces cas là. Par exemple, si on rentre l'expression
* une nouvelle definition remplace la précédente mais ne l'efface pas, (on ne met pas une valeur à jours)
let x = 1.5 in x+1
Caml répond :
# let x = 1.5 in <u>x</u>+1 ;;
This expression has type float but is here used with type int
(Remarquez le <tt>x</tt> souligné pour nous dire d'où vient l'erreur...)


==== Définitions ====
::'''Remarque :''' en Caml, on peut déclarer une variable locale à une expression avec la syntaxe "<tt>let x= expr in expr</tt>".


L'utilisateur peut rajouter des définitions dans l'environnement grâce au mot clé <tt>let</tt>
===Les valeurs fonctionnelles===


* <tt>let nom = expr</tt> permet de rajouter une définition simple dans l'environnement. Exemple : <tt>let n = 42</tt>
Les exemples du paragraphe précédent ne devraient pas trop vous surprendre. La vraie nouveauté est que même les fonctions sont des valeurs.
* <tt>let nom1 = expr1 and nom2 = expr2</tt> permet de rajouter plusieurs définitions simultanément. Exemple : <tt>let a=1 and b=2</tt>
length --> valeur de type "fonction des chaînes vers les entiers"
* <tt>let rec nom1 = expr1 and nom2 = expr2</tt> permet de rajouter des définitions mutuellement récursives.


On peut toujours annoter la définition de types de données. Ceci évite à Caml d'avoir à deviner le type que l'on souhaite et permet de préciser la fonction :
Une valeur de type fonctionnel peut être définie de la manière suivante :
<source lang="ocaml">
fun x y -> 2*(x+y)
let n:int = 42
Ceci ressemble à une définition mathématique usuelle :
let rec f (n:int) : string = if (n<1) then "" else "*" ^ (f (n-1))
<center> La fonction qui à <math>x</math> et <math>y</math> associe <math>2\times(x+y)</math>, que l'on note habituellement <math>x,y\mapsto 2\times(x+y)</math></center>
</source>


Si une définition utilise le même nom qu'une définition précédente, la nouvelle définition écrase l'ancienne. Par exemple :
Remarquez que cette fonction n'a pas de nom. Les langages impératifs tels que C, ou Pascal ne permettent pas de définir une fonction sans lui donner de nom.
<source lang="ocaml">
# let n=42 ;;
val n : int = 42
# let n=3.1415 ;;
val n : float = 3.1415
# n ;;


* : float = 3.1415
</source>


==== Définitions locales ====
Une telle fonction avec 2 arguments pourra être appliquée pour obtenir une nouvelle valeur. Dans l'exemple précédent, la fonction avaient deux arguments entiers et donnait un résultat de type entier. Si on donne le nom <tt>f</tt> à cette fonction :
let f = fun x y -> 2*(x+y)
on pourra obtenir de nouvelles valeurs telles que :
f 1 2


Il est possible de modifier l'environnement de manière temporaire (''locale''). On utilise alors <tt>let nom = expr1 in expr2</tt>. Ceci ajoute la définition <tt>nom = expr1</tt> dans l'environnement, mais seulement pour l'évaluation de l'expression <tt>expr2</tt>.
::'''Notez bien''' que pour appliquer une fonction à ses arguments, les parenthèses ne sont en général pas nécessaires, et qu'on n'utilise pas de "<tt>,</tt>" pour séparer les arguments.
<source lang="ocaml">
# let x =
let y = 42 in
y/2 ;;
val x : int = 21
# x ;;


* : int = 21
# y ;;
Unbound value y
</source>


===Notion d'environnement===
=== Les fonctions ===


Comme tous les langages de programmation, Caml garde en mémoire une liste de définitions. Chacune de ces définitions met en relation un nom ("<tt>x</tt>", "<tt>n</tt>", "<tt>length</tt>", ...) et une valeur ("<tt>3.7</tt>", "<tt>42</tt>", "<tt>fun s -> ...</tt>").
Caml utilise la notation mathématique pour écrire les types des fonctions. Si <tt>f</tt> est une fonction avec un seul argument de type <tt>string</tt> et dont la valeur de retour est de type <tt>int</tt> (la fonction <tt>length</tt> par exemple), le type de <tt>f</tt> est noté <tt>string -> int</tt>. Dans l'interprète Caml :
<source lang="ocaml">
# String.length ;;


* : string -> int = <fun>
Lorsque l'on lance Caml, l'environnement contient déjà de nombreuse fonctions prédéfinies : <tt>max</tt>, <tt>min</tt>, <tt>mod</tt>, <tt>+</tt>, <tt>float_of_int</tt>, ... Une occurrence d'un nom de variable de l'environnement qui est en position de ''variable libre'' sera remplacé par la valeur correspondante.
</source>
Nous indique que la fonction <tt>length</tt> est bien de type <tt>string->int</tt>.


Une fonction est définie comme une variable avec un <tt>let</tt> et en utilisant le mot clé <tt>fun</tt> :
<source lang="ocaml">
# let f = fun n -> n+1;;
val f : int -> int = <fun>
# let racine = fun n -> int_of_float (sqrt (float_of_int n));;
val racine : int -> int = <fun>
</source>


Une fonction avec plusieurs argument est définie de la même manière avec
====Définitions====
<source lang="ocaml">
# let moyenne3 = fun x y z -> (x +. y +. z) /. 3. ;;
val moyenne3 : float -> float -> float -> float = <fun>
</source>


Le type d'une fonction à plusieurs argument est noté en mettant plusieurs types à gauche de la flèche <tt>-></tt>, comme ci dessus : <tt>type_arg1 -> type_arg2 -> type_arg3 -> type_resultat</tt>.
L'utilisateur peut rajouter des définitions dans l'environnement grâce au mot clé <tt>let</tt>
* <tt>let ''nom'' = ''expr''</tt> permet de rajouter une définition simple dans l'environnement. Exemple : <tt>let n = 42</tt>
* <tt>let ''nom1'' = ''expr1'' and ''nom2'' = ''expr2''</tt> permet de rajouter plusieurs définitions simultanément. Exemple : <tt>let a=1 and b=2</tt>
* <tt>let rec ''nom'' = ''expr''</tt> permet de rajouter une définition récursive dans l'environnement. Exemple : <tt>let rec f n = if (n<1) then 0 else n+ f (n-1)</tt>
* <tt>let rec ''nom1'' = ''expr1'' and ''nom2'' = ''expr2''</tt> permet de rajouter des définitions mutuellement récursives.


'''Remarques :'''
Dans chacun des cas précédents, on peut annoter la définition de types de données. Ceci évite à Caml d'avoir à deviner le type que l'on souhaite et permet de préciser la fonction :
let n:int = 42
let rec f (n:int) : string = if (n<1) then "" else "*" ^ (f (n-1))


* notez l'absence de mot clé <tt>return</tt> et l'absence de virgules pour séparer les différents arguments !
* Le mot clé <tt>fun</tt> permet de définir des fonctions sans leur donner de nom, ce qui n'est pas possible dans les langages comme C ou Java...


On peut également utiliser un raccourci pour définir une fonction sans utiliser le mot clé <tt>fun</tt> : il suffit de mettre les arguments de la fonction avant le signe égal :
Si une définition utilise le même nom qu'une définition précédente, la nouvelle définition écrase l'ancienne. Par exemple :
<source lang="ocaml">
# let n=42 ;;
val n : int = 42
# let f n = n+1;;
val f : int -> int = <fun>
# let n=3.1415 ;;
# let racine n = int_of_float (sqrt (float_of_int n));;
val n : float = 3.1415
val racine : int -> int = <fun>
# n ;;
# let moyenne3 x y z = (x +. y +. z) /. 3. ;;
- : float = 3.1415
val moyenne3 : float -> float -> float -> float = <fun>
</source>


Pour appliquer une fonction à des arguments, on écrit simplement
<source lang="ocaml">
moyenne3 13.5 17.0 -0.333
</source>
Il n'y a ni parenthèse, ni virgule. Les parenthèses peuvent cependant être nécessaires pour différencier
<source lang="ocaml">
moyenne3 13.5 17.0 (-0.333 *. 6.)
</source>
et
<source lang="ocaml">
(moyenne3 13.5 17.0 -0.333) *. 6.
</source>
ou pour que Caml lise correctement
<source lang="ocaml">
moyenne3 13.5 (17.0 -. 12.0) -0.333
</source>


====Définitions locales====
==== Fonctions récursives ====


Si on essaie de définir une fonction récursivement :
Il est possible de modifier l'environnement de manière temporaire (''locale''). On utilise alors <tt>let ''nom'' = ''expr1'' in ''expr2''</tt>. Ceci ajoute la définition <tt>''nom'' = ''expr1''</tt> dans l'environnement, mais seulement pour l'évaluation de l'expression <tt>''expr2''</tt>.
<source lang="ocaml">
# let x =
let y = 42 in
let f n = if (n<1) then 0 else n + f (n-1)
</source>
y/2 ;;
Caml répond
val x : int = 21
<source lang="ocaml">
# x ;;
Error: Unbound value f
- : int = 21
</source>
# y ;;
car <tt>f</tt> n'est pas présente dans l'environnement.
Unbound value y


Pour rajouter <tt>f</tt> comme variable active (sans valeur) et pouvoir faire une définition récursive, il faut utiliser le mot clé <tt>rec</tt> :
Les définitions locales peuvent elles aussi être multiples (<tt>let ... and ... in</tt>), récursives (<tt>let rec ... in</tt>), mutuellement récursives (<tt>let rec ... and ... in</tt>). Elles acceptent également les annotations de type...
<source lang="ocaml">
# let rec f n = if (n<1) then 0 else n + f (n-1);;
val f : int -> int = <fun>
</source>


Les définitions locales peuvent elles aussi être récursives (<tt>let rec ... in ...</tt>) ou mutuellement récursives (<tt>let rec ... and ... in ...</tt>). Elles acceptent également les annotations de type...
===Transparence référentielle===


==== fonctions d'ordre supérieur ====
Une des idées importantes en programmation fonctionnelle est la notion de « transparence référentielle » : grosso-modo, cela veut dire que l'on a toujours le droit de remplacer une expression par sa valeur sans changer le sens du programme. Bien que ceci semble évident, ce n'est pas vrai dans un langage tels que le langage C...


TODO
Pour comprendre pourquoi ceci n'est pas vrai dans un programme Pascal, considérez la fonction suivante
function f (x:integer) : integer ;
begin
writeln('Salut...');
y:=0; { y est une variable globale }
f:=x+1;
end;
La valeur de <tt>f(3)</tt> est <tt>4</tt>, mais on ne peut pas remplacer <tt>f(3)</tt> par <tt>4</tt> sans changer le comportement du programme...


=== Les listes ===
C'est cette propriété qui facilite grandement la formalisation des langages fonctionnels, car on peut appliquer le principe mathématique
<center> si <math>x=y</math> alors <math>f(x)=f(y)</math>.</center>


Le type des listes est fondamental en programmation fonctionnelle. D'une certaine manière, on peut dire qu'ils remplacent les tableaux que l'on utilise constamment en programmation impérative.
==Les types==


<u>Exercice :</u> quels sont les différences importantes (utilisation, complexité, mémoire, ...) entre les tableaux et les listes ?
===Les types atomiques===


En Caml, une liste est représentée entre crochets, et les éléments sont séparés par des points-virgules :
Caml (et les autres langages fonctionnels typés) possèdent plusieurs types de base tels que :
<source lang="ocaml">
* les booléens : type <tt>bool</tt> (valeur <tt>true</tt> et <tt>false</tt>),
# let une_liste = [ 1 ; 2 ; 2 ; -1 ; 0 ] ;;
* les entiers : type <tt>int</tt> pour les entiers signés compris entre <math>-2^{30}</math> et <math>2^{30}-1</math>. (Les entiers Caml sont stockés sur 31 bits...)
val une_liste : int list = [1; 2; 2; -1; 0]
* les flottants : type <tt>float</tt> pour les réels en virgule flottante,
# let une_autre_liste = [ "coucou" ; "je m'appelle" ; "Bob" ] ;;
* les caractères : type <tt>char</tt> (notés entre guillemets simples)
val une_autre_liste : string list = ["coucou"; "je m'appelle"; "Bob"]
* le type unité : type <tt>unit</tt>, dont l'unique valeur est <tt>()</tt>. (Nous verrons que ce type a une utilité...)
</source>


Comme vous pouvez le voir dans l'exemple au dessus, le type des listes d'entiers s'appelle "<tt>int list</tt>", le type des listes de flottants s'appelle "<tt>float list</tt>" et le type des listes de listes d'entiers s'appelle ... "<tt>int list list</tt>".
Même s'il ne s'agit pas vraiment d'un type atomique, nous pouvons également ajouter le type des chaînes de caractères : type <tt>string</tt> (notées entre guillemets doubles).


Contrairement au cas des tuples, les éléments d'une liste doivent tous avoir le même type :
Caml définit de nombreuse fonctions sur ces types de données :
<source lang="ocaml">
* opérations arithmétiques
# let une_non_liste = [ 1 ; "Toto" ; 1.3 ] ;;
* fonctions mathématiques usuelles (logarithme, sinus, ...)
This expression has type string but is here used with type int
* ...
</source>


<u>Attention :</u> quel est le type de la liste suivante ?
::'''Remarques :''' les opérations booléennes ("<tt>&&</tt>" pour le « et » et "<tt>||</tt>" pour le « ou ») fonctionnent de à gauche à droite, et l'argument de droite n'est évalué que si c'est nécessaires. Par exemple <tt>true || (0 = 1/0)</tt> donne la valeur <tt>true</tt>, alors que que <tt>false || (0=1/0)</tt> lève l'exception <tt>Division_by_zero</tt>.
<source lang="ocaml">
# let une_liste_bizarre = [ 1 , 2 , 3 ] ;;
</source>


On peut ajouter un élement devant une liste avec l'opérateur "<tt>::</tt>" et la concaténation des listes s'obtient avec l'opérateur "<tt>@</tt>".
===Les fonctions===
<source lang="ocaml">
# 5::[1;2;3;4] ;;
- : int list = [5; 1; 2; 3; 4]
# [-1;-2;-3;-4] @ [1;2;3;4] ;;
- : int list = [-1; -2; -3; -4; 1; 2; 3; 4]
</source>
<u>Attention :</u> la complexité de l'opération "<tt>::</tt>" ne dépend pas de la taille des listes mises en jeux. Par contre, la complexité de l'opération "<tt>@</tt>" dépend proportionnellement de la taille de son premier argument. Il faut donc toujour privilégier "<tt>::</tt>" par rapport à "<tt>@</tt>". En particulier, il est en général mauvais de rajouter des élément en fin de liste en utilisant "<tt>l @ [e]</tt>".


=== Filtrage, première partie : tuples et listes ===
Caml utilise la notation mathématique pour écrire les types des fonctions. Si <tt>f</tt> est une fonction avec un seul argument de type <tt>string</tt> et d
ont la valeur de retour est de type <tt>int</tt> (la fonction <tt>length</tt> par exemple), le type de <tt>f</tt> est noté <tt>string -> int</tt>. Dans l'in
terprète Caml :
# length ;;
- : string -> int = <fun>
Nous indique que la fonction <tt>length</tt> est bien de type <tt>string->int</tt>.


La notion de ''filtrage'', aussi appelé "pattern-matching" est un outils clé pour la programmation en Caml. L'idée est de décomposer une valeur en "sous-valeurs".
Une nouveauté des langages fonctionnels par rapports à Pascal est que <tt>string->int</tt> est un type comme les autre, et on peut donc créer une fonction d
e type <tt>string -> (string -> int)</tt>. Prenons par exemple la fonction suivante :
let f:string->int = fun s ->
let t = "prefix-" in
length (t ^ s) (* "^" est l'opérateur de concaténation des chaînes. *)
Cette fonction va calculer la longueur de son argument, auquel on a rajouter le préfixe <tt>"prefix-"</tt>.


==== Filtrage sur les tuples ====
Si on veut pouvoir changer le préfixe que l'on rajoute devant <tt>s</tt>, on peut faire la fonction suivante :
let g:string -> ( string -> int) =
fun prefix ->
fun s -> length (t^s)
La fonction <tt>f</tt> précédente aurait pu être obtenue grâce à "<tt>g "prefix-"</tt>".


Comme une paire est de la forme <tt>(x,y)</tt>, on peut décomposer une valeur de type <tt>a*b</tt> en deux sous valeurs. Voici un exemple de syntaxe : le code de la fonction <tt>fst</tt> :
::'''Remarque :''' Caml n'utilise pas les parenthèses dans ce cas là : il notera "<tt>string -> string -> int</tt>".
<source lang="ocaml">
let premier p = match p with
(x,y) -> x
</source>


Pour évaluer "<tt>premier e</tt>", Caml regarde le premier argument <tt>e</tt> et essaie de faire coïncider "<tt>(x,y)</tt>". S'il y arrive, alors il renvoie la valeur "<tt>x</tt>". Le typage de Caml inférera automatique que "<tt>p</tt>" doit être une paire, et il n'y a donc pas besoin de prévoir un cas par defaut.


On peut maintenant récupérer la deuxième composante d'un triplet de la manière suivante :
Nous verrons un peu plus tard que les fonctions peuvent avoir des arguments qui sont eux mêmes des fonctions.
<source lang="ocaml">
let second3 t = match t with
(_, y, _) -> y
</source>
Notez l'utilisation de "<tt>_</tt>" pour donner la position d'une sous-valeur qui ne sert à rien.


Toutes les variables présentes dans le ''motif'' (la partie à gauche de la flèche) se retrouvent dans l'environnement avec pour valeur, la sous-valeur appropriée. <u>Attention,</u> ces variables ne se retrouvent dans l'environnement que dans la partie droite de la flèche.
::<u>Exercice :</u> essayez de trouver une fonction de type <tt>(int->int) -> int</tt>.
Ces variables remplacent (temporairement) les variables de même nom qui pouvaient se trouver dans l'environnement. Par exemple, la fonction :
<source lang="ocaml">
let f x = match x with
(x,_) -> x
</source>
est exactement la fonction <tt>fst</tt>.


==== Filtrage sur les listes ====


On peut décomposer une liste en deux sous-valeurs :
===Les paires===


* le premier élément de la liste,
Un autre type intéressant est celui des paires de valeur. Par exemple, le type "<tt>int * string</tt>" est compose de valeur "<tt>(3,"toto")</tt>". On peut accéder au premier élément d'une paire avec la fonction "<tt>fst</tt>" et au second élément avec la fonction "<tt>snd</tt>".
* la suite de la liste.
let paire = ("Bonjour !" , 42*42) in (snd paire)
donnera la valeur 1764.


Autrement dit, si la liste <tt>l</tt> n'est pas vide, on peut la décomposer en <tt>e::q</tt>. Comme il est possible que la liste <tt>l</tt> soit vide, il y a en fait deux manière de décomposer <tt>l</tt> :
Ce type correspond exactement à la notion de ''produit cartésien'' utilisée en mathématiques :
<center><math> A \times B \quad=\quad \{ (a,b) \ |\ a\in A \,,\,b\in B\} </math></center>


* soit <tt>[]</tt> (sans aucune sous-valeur),
Les fonctions <tt>fst</tt> et <tt>snd</tt> sont généralement appelées ''projections'' :
* soit <tt>e::q</tt> (avec deux sous-valeurs).
<center><math> \pi_1 : A\times B \to A \qquad (a,b) \mapsto a</math></center>
(et pareillement pour la projection sur l'élément de droite...)


Par exemple, voici une manière de tester si une liste est vide :
<source lang="ocaml">
let vide l = match l with
[] -> true
| x::xs -> false
</source>
Les différents cas sont séparés par le symbole "<tt>|</tt>".


On peut également décomposer une liste en 3 sous-valeurs : le premier élément, le second élément et la suite de la liste. Dans ce cas, il a trois cas pour une liste arbitraire :
Ce type de données particulièrement utile lorsque l'on veut écrire une fonction qui renvoie 2 ou 3 valeurs. On verra par la suite qu'il existe d'autre types plus appropriés si l'on veut avoir de nombreux champs, et s'il l'on veut leur donner un nom. (Dans une paire, la première composante et la seconde composante n'ont pas de nom propre...)


* elle est vide <tt>[]</tt> (aucune sous-valeur),
Notez également que les fonctions a plusieurs arguments n'utilisent en général pas de produit cartésien. Pour Caml :
* elle ne contient qu'une seul élément <tt>[e]</tt> (une sous-valeur),
* une fonction de type <tt>(A*B) -> C</tt> est une fonction à un seul argument, mais son argument est une paire,
* elle contient un premier élément, un deuxième élément, et une suite <tt>a::b::q</tt> (trois sous-valeurs).
* une fonction de type <tt>A -> B -> C</tt> est une fonction à deux arguments séparés. (Plus précisément, c'est une fonction à un argument qui renvoie une fonction à un argument.)


On peut par exemple écrire :
<source lang="ocaml">
let deux_elements_ou_plus l = match l with
[] -> false
| [a] -> false
| a::b::q -> true
</source>


Comme on peut toujours décomposer une valeur en une sous-valeur (la valeur elle même), on peut toujours avoir un motif de la forme <tt>x</tt>. Toutes les valeurs pourront être décomposées de cette manière, et il faut donc que ce motif soit le dernier. (Sinon, les motifs suivants ne servent à rien...) On parle de motif ''universel''. (On peut utiliser <tt>_</tt> comme motif universel.) Par exemple, on peut réecrire l'exemple précédent :
:<u>Exercice : </u> cherchez une manière de passer d'une fonction de type <tt>A*B -> C</tt> à une fonction de type <tt>A -> B -> C</tt>, et vice-versa. Ces transformations s'appellent la ''curryfication'' et la ''decurryfication''.
<source lang="ocaml">
let deux_elements_ou_plus
_::_::_ -> true
| _ -> false
</source>


==== Compléments ====
:Écrivez les fonctions <tt>curry :: ('a*'b -> 'c) -> ('a -> 'b -> 'c)</tt> et <tt>un curry : ('a -> 'b -> 'c) -> ('a*'b -> 'c)</tt> en Caml.


# On peut ajouter des constantes dans les motifs. Le motif ne sera utilisé que lorsque la sous-valeur correspondantes a la valeurs correspondante. Par exemple, si on modélise les entiers de Gauss (« entiers complexes ») par des paires d'entiers, on peut définir :
===Les listes===
<source lang="ocaml">
let imaginaire_pur c = match c with
(0,0) -> false
| (0,_) -> true
| _ -> false
</source>
# On peut grouper des motifs différents pour faire la même chose. Il faut que les variables qui apparaissent dans les motifs soient les mêmes, et qu'elles aient le même type :
<source lang="ocaml">
let f x = match x with
0::l | 1::_::l | 2::_::_::l -> l (* on groupe 3 motifs en une seule ligne *)
| [] -> []
| x::_ -> [x]
</source>
# on peut « garder » les motifs" par une condition avec le mot clé "<tt>when</tt>"
<source lang="ocaml">
let g x = match x with
x::xs when (List.lenght (f xs) = 1) -> true
| _ -> false
</source>
<u>Attention</u> dans le cas où vous utilisez <tt>when</tt> : il est conseillé de terminer votre filtrage avec un motif universel pour être sûr que vous n'avez pas oublié de cas.
# comme on définit souvent des fonctions par filtrage, Caml autorise la syntaxe
<source lang="ocaml">
function
pattern1 -> expr1
| pattern2 -> expr2
| ...
</source>
plutôt que
<source lang="ocaml">
fun x -> match x with
pattern1 -> expr1
| pattern2 -> expr2
| ...
</source>
Remarquez que l'on ne peut faire ça que pour une fonction à une seule variable...
# fonction par filtrage : s'il n'y a qu'un seul motif, on peut alors utiliser le mot clé "<tt>fun</tt>" habituel, ce qui permet la définition d'une fonction à plusieurs arguments. (Mais dans ce cas, les parenthèses autour des tuples sont obligatoires.)
<source lang="ocaml">
# let h = fun (x,y) z (u,v,w) -> x+y+z+v ;;
val h : int * int -> int -> 'a * int * 'b -> int = <fun>
</source>


=== Le polymorphisme ===
Le type des listes est fondamental en programmation fonctionnelle. D'une certaine manière, on peut dire qu'ils remplacent les tableaux que l'on utilise constamment en programmation impérative.


:<u>Exercice : </u> quels sont les différences importantes (utilisation, complexité, mémoire, ...) entre les tableaux et les listes ?


=== Transparence référentielle ===


=== Les types algébriques ===
En Caml, une liste est représentée entre crochets, et les éléments sont séparés par des points-virgules :
# let une_liste = [ 1 ; 2 ; 2 ; -1 ; 0 ] ;;
val une_liste : int list = [1; 2; 2; -1; 0]
# let une_autre_liste = [ "coucou" ; "je m'appelle" ; "Bob" ] ;;
val une_autre_liste : string list = ["coucou"; "je m'appelle"; "Bob"]


On peut défnir un synonyme de type avec le mot clef <tt>type</tt>
Contrairement au cas du produit cartésien de types, les éléments d'une liste doivent tous avoir le même type :
Exemple :
# let une_non_liste = [ 1 ; "Toto" ; 1.3 ] ;;
<source lang="ocaml">type point = float*float</source>
This expression has type string but is here used with type int


==== Types énumérés ====


:'''Attention : ''' quel est le type de la liste suivante ?
# let une_liste_bizarre = [ 1 , 2 , 3 ] ;;




On donne tous les éléments du type
La concaténation des listes s'obtient avec l'opérateur <tt>@</tt>, et il existe (dans la bibliothèque "<tt>List</tt>" les fonctions suivantes
Exemple :
* <tt>length</tt>,
<source lang="ocaml">type jours = Lundi | Mardi | Mercredi | ...</source>
* <tt>hd</tt> pour récupérer le premier élément d'une liste,
* <tt>tl</tt> pour récupérer la ''queue'' d'une liste (toute la liste, sans son premier élément).


Type avec 7 éléments. Les éléments commencent forcément par une majuscule.


On peut comparer des éléments avec "=", "<" et ">" (< et > sont à éviter, il vaut mieux
====Définitions récursives sur les listes====
écrire une fonction pour comparer) On peut aussi faire du filtrage.


Exemple:
Il est possible (même si ce n'est pas le plus pratique, ni le plus efficace) d'écrire des fonctions récursives sur les listes en utilisant la fonction <tt>lenght</tt>. Une meilleur solution consiste à utiliser les fonctions <tt>List.hd</tt> et <tt>List.tl</tt>
<source lang="ocaml">
let week_end j = match j with
Samedi -> true
| Dimanche -> true
| _ -> false
</source>


==== Construction de types paramètres ====
:'''Remarque :''' Caml possède plusieurs fonctions <tt>length</tt> : notamment une pour les listes et une pour les chaînes de caractères. Pour accéder à ces fonctions (qui ne sont pas dans la bibliothèque standard), il faut utiliser :
:* <tt>List.length</tt> pour la longueur des listes,
Exemple : type des transformations de l'espace avec :
:* <tt>String.length</tt> pour la longueur des chaînes.
- Translation
:Vous pouvez aussi ''ouvrir'' toute la bibliothèque correspondante en utilisant la ligne <tt>open List</tt> au début de votre programme pour avoir accès à toutes les fonctions de la bibliothèque sur les listes...
- Rotation
- Symetrie
On aura alors des constructeurs avec des paramètres :


<source lang="ocaml">
Je ne donne volontairement pas d'exemple, car ce n'est pas la manière usuelle de procéder. Utilisez plutôt le ''filtrage'' (''"pattern matching"'') décrit plus loin, ou les fonctions prédéfinies comme <tt>List.fold_left</tt> ou <tt>List.fold_right</tt> (qui seront vues en TD et TP).
type transformation = Rotation of int*point
| Translation of point
| SymCentrale of point
| Homothetie of float*point
</source>


On n'a pas donné explicitement tous les éléments, mais on les a donné implicitement.
Exemple :
<source lang="ocaml">
let rien = Translation (0.0,0.0)
</source>
Autre exemple :
<source lang="ocaml">
let fait quelquechose t = match t with
Rotation(a, (x, y) −> a mod 360 != 0
| Translation(x, y) −> x != 0.0 || y != 0.0
| SymCentrale(x, y) −> true
| Homothetie(x, (−, −)) −> r != 1.0
</source>


==== Les types récursifs ====
===Filtrage, première partie===


En Caml
==Le polymorphisme==
<source lang="ocaml">
type node = Node of int*node
| Vide
</source>
exemple d'éléments de "node" :
- Vide
- Node(0,Vide)
- Node(1,Node(2,Node(3, Vide)))


Le type "node" est un type récursif. Comme les fonctions récursivent doivent avoir des cas
=Les types avec constructeurs (type ''sommes'')=
de base, les types récursifs doivent avoir des constructeurs "de base" (non récursifs). On peut
comparer des éléments avec "=". On peut aussi utiliser le filtrage.


Exemple :
==Déclaration des types sommes==
<source lang="ocaml">
let rec taille l = match l with
Vide -> 0
|Node(_,l)->1+taille l
</source>

La fonction taille sur les listes Caml suit le même schéma avec les abréviations Caml.
<source lang="ocaml">
let rec taille l = match l with
[]->0
| a ::l->1+taille
</source>
Ceci explique la différence entre :: (constructeur) et @ (fonction récursive)

Exemple de type récursif, les arbres binaires :
<source lang="ocaml">
type bin = Vide | Noeud of int*bin*bin
</source>
arbre naire :
<source lang="ocaml">
type arbre n_aire =
Vide|Node of int ∗ (arbre_naire list)(bancal)
</source>


== Les structures ==
==Filtrage==


==Types récursifs==


== La récursivité terminale ==


=Les structures=


== Les exceptions ==


=Les exceptions=


== Aspects impératifs dans Caml ==
=Les librairies=




=Les modules=
== Les modules ==

Dernière version du 13 février 2013 à 12:11

Ce wiki est un complément de cours pour le cours « info-421 : programmation fonctionnelle ». La participation au wiki est fortement encouragée, et deviendra peut-être obligatoire...

Pour pouvoir modifier les pages, inscrivez-vous (lien en haut à droite) pour obtenir un login et mot de passe. (Utilisez votre vrai nom...)

Je vous conseille d'aller voir ce guide pour vous familiariser avec les wikis.


Exercice : si vous n'en avez pas, créez-vous un compte et essayez de modifier cette page (correction de fôtes d'aurtograffe, rajout de détails, mise en page, ...)

Vous pouvez aussi utiliser la page de discussion pour ... discuter. (Ou poser des questions, faire des commentaires etc.)


Détails techniques

Compléments de cours, TD et TP

Installer Ocaml

Si vous voulez installer OCaml sur votre ordinateur :

  • Sous Linux : c'est la solution idéale. Il existe probablement des paquets pour votre distribution. Pour Ubuntu, pour avoir un environnement similaire à ce que vous aurez dans les salles informatiques, installez les paquets ocaml, ocaml-core, ocaml-mode, tuareg-mode et emacs.
  • Sous MacOS : il semblerait qu'il y ait des paquets MacPorts pour ocaml, emacs et tuareg-mode.el.
  • Sous Windows : je vous renvoie au tutoriel de Jean-Paul Roy : Installation de OCaml (sur Windows XP). Je n'ai malheureusement (??) pas accès à une machine avec Windows (98/2000/XP/Vista/7), je ne pourrais donc pas beaucoup vous aider.

Contactez moi si vous avez des problèmes.

Pour les exemples simples, vous pouvez aussi utiliser OCaml en ligne

Références supplémentaires

Cours

TD et TP

Introduction

Pour paraphraser un collègue dont je ne retrouve pas le nom :

Attention : il ne s'agit pas d'un cours de programmation fonctionelle

Il s'agit plutôt d'un cours de programmation fonctionnelle...

Petit historique censuré

Je ne parlerais pas des langages pré-historiques (cartes perforées, λ-calcul, machines de Turing, ...)

D'après ce site, qui recense la plupart des langages de programmation, il y aurait plus de 2500 langages ! Voici donc une petite liste de langages importants :

  • années 40 : langages d'assemblage (assembleurs). Aussi vieux que les ordinateurs eux-mêmes. Chaque langage d'assemblage est spécifique à une famille de processeurs, ce qui rend les programmes difficiles à porter. (Càd à modifier pour les faire marcher sur d'autres ordinateurs.)
  • FORTRAN (1957, toujours utilisé par les numériciens et physiciens) et COBOL (1960, toujours utilisé en gestion). Ces langages ont connus des évolutions mais restent archaïques par leur conception.
  • LISP : inventé par John McCarthy en 1958. C'est le premier langage fonctionnel. Toujours utilisé (sous différentes formes), en particulier en intelligence artificielle. Ce langage est basé directement sur le λ-calcul de Church.
  • ALGOL (1958, a inspiré de nombreux langages depuis : C, pascal, ...) Le but était de réparer certains défauts des langages de type FORTRAN. (Programmation structurée, blocs, ...)
  • Pascal (1975).
  • C (1972). Toujours très utilisé, sous différentes variantes (notamment C++).
  • Prolog (1972) : programmation logique, paradigme nouveau de programmation. Toujours utilisé par une petite communauté.
  • ML (fin des années 1970 ?), qui ajoute une notion de type que LISP n'avait pas.
  • Smalltalk (fin des année 1983), début de la programmation objet.
  • 1983 : ADA.
  • année '80 : Caml (1987), puis CamlLight(1990), puis OCaml(1996), développé

à l'INRIA. (Dernière version en octobre 2012.)

  • années '90 : Python (version 1.0 en 1994).
  • années '90 : PHP (version 1.0 en 1995).
  • années '90 : Java (version 1.0 en 1996).

Je vous conseille d'aller voir le graphique suivant : Computer Languages Timeline (ou découpé en pages A4).

Fonctionnel ??

L'adjectif fonctionnel a au moins deux sens :

  1. qui fonctionne, en état de marche,
  2. qui se rapporte aux fonctions.

Les langages fonctionnels sont bien entendus fonctionnels dans le premier sens, mais c'est surtout le second sens qui nous intéresse. Les langages tels que Pascal, Ada ou C sont qualifié, par opposition, d'impératifs.

Un des slogans de la programmation fonctionnelle en général est

Les fonctions sont des valeurs comme les autres

et c'est de là que vient la terminologie... En particulier, il n'y a pas de différence entre les instructions (qui ont un effet) et les expressions (qui ont une valeur). Par exemple, en Python,

x = x+1

ou

if delta < 0:
    print("Il n'y a pas de solution")

sont des instructions : on ne peut pas les mettre dans une variable.

Comme nous le verront, cela a des conséquences sur l'expressivité du langage et la manière de programmer.

Le langage (O)Caml

Le langage OCaml est développé par l'INRIA (Institut national de recherche en informatique et automatique). C'est un successeur de CamlLight.

Le nom Caml est formé des initiales de "Categorical Abstract Machine Language", et le langage lui même appartient à la famille de ML ("Meta Language"). C'est un langage fonctionnel strict (nous verrons ce que cela veut dire), statiquement typé (nous verrons ce que cela veut dire) qui supporte plusieurs styles de programmation :

  • fonctionnel bien sûr,
  • mais aussi impératif,
  • objet également (c'est le « O » de OCaml).

Dans ce cours, nous utiliserons principalement le style fonctionnel, et un peu le style impératif en fin de semestre.

Voici quelques aspects importants du langages que nous essayeront d'aborder pendant le cours :

  • fonctions comme valeurs,
  • types de données algébriques
  • polymorphisme,
  • système d'exceptions,
  • support pour des références et des données mutables (programmation « impure »),
  • système de modules et de foncteurs,
  • ...

La notion de récursivité sera fondamentale pendant toute la durée du cours...

Un aspect intéressant du langage est que c'est :

  • un langage interprété (avec l'interpréteur OCaml),
  • soit un langage compilé en bytecode (code binaire indépendant de l'architecture),
  • soit un langage compilé optimisé en binaire (dépendant de l'architecture).

Autres langages fonctionnels

Il existe de nombreux autres langages fonctionnels. Par exemple :

  • SML (Standard ML) : Wikipedia, autre dialecte de la famille ML.
  • LISP, dont les deux dialectes principaux sont :
  • Haskell : Wikipedia (inspiré en grande partie de Miranda).

Plusieurs langages impératifs intègrent maintenant des aspects propres des langages fonctionnels : Python, Scala, ...

Applications concrètes

Voici quelques exemples de logiciels développés en OCaml :

La viabilité du paradigme fonctionnel se retrouve également dans le langage Erlang (Wikipedia), un langage fonctionnel développé par Ericsson pour la programmation concurrente de systèmes temps réels.

Objectifs du cours

  1. être capable de définir des fonctions récursives, et comprendre ce qu'elles font
  2. comprendre le typage, les types algébriques et le polymorphisme à la ML
  3. pouvoir définir des fonction d'ordre supérieur pour modulariser votre code
  4. être capable de décomposer un problème
  5. commencer à réfléchir à la complexité de vos programmes

Premiers pas en Caml

Un des slogans de la programmation fonctionnelle est tout est une valeur, ou plus précisément,

Toute expression a une valeur.

Cette idée n'est pas présente en Python où l'on distingue les expressions ("3*x+f(2)" par exemple) et les instructions ("if n>22: a=12 else: a=13" par exemple). En Python, les instructions sont en général séparées par des retours à la ligne et sont exécutées en séquence. Les expressions sont juste calculées, et ne peuvent pas être séquentialisées.

Dans un langage purement fonctionnel, le ";" n'existe pas, et il n'y a que des valeurs...

Les valeurs sont formées en utilisant :

  • les fonctions du langage (addition, multiplication, opérateurs logiques),
  • des constructions du langage (if ... then ... else ... par exemple),
  • les relations mathématiques (égalité, plus grand), ... qui sont juste des fonctions renvoyant des booléens,
  • les constantes du langage,
  • les variables de l'environnement,
  • les valeurs (en particulier les fonctions) définies par le programmeur.

Bien entendu, comme Caml est typé, il faut respecter les types.

Les types atomiques

Caml (et les autres langages fonctionnels typés) possèdent plusieurs types de base tels que :

  • les booléens : type bool (valeur true et false) et les opérations not, && (et) et || (ou).
  • Les entiers : type int pour les entiers signés compris entre et . (Les entiers Caml sont stockés sur 31 bits...) Les opérations associés sont +, -, *, / (pour la division entière) et mod (pour le modulo).
  • Les flottants : type float pour les réels en virgule flottante. Les opérations usuelles sont +., -., *. et /..
  • Les caractères : type char (notés entre guillemets simples).
  • Le type unité : type unit, dont l'unique valeur est (). (Nous verrons que ce type a une utilité...)

Même s'il ne s'agit pas vraiment d'un type atomique, nous pouvons également ajouter le type des chaînes de caractères : type string (notées entre guillemets doubles).

<source lang="ocaml"> (* exemples ... *) </source>

Remarques : les opérations booléennes ("&&" pour le « et » et "||" pour le « ou ») fonctionnent de à gauche à droite, et l'argument de droite n'est évalué que si c'est nécessaire. Par exemple true || (0 = 1/0) donne la valeur true, alors que que false || (0=1/0) lève l'exception Division_by_zero.

définitions et environnement

Comme tous les langages de programmation, Caml garde en mémoire une liste de définitions. Chacune de ces définitions met en relation un nom ("x", "n", "length", ...) et une valeur ("3.7", "42", "fun s -> ...").

Lorsque l'on lance Caml, l'environnement contient déjà de nombreuse fonctions prédéfinies : max, min, mod, +, float_of_int, ...

Certaines des variables de l'environnement correspondent à des paramètres de fonction en cours de définition. Ces variables n'ont pas de valeur, mais seulement un type.

Environnement

  • Liste de paires nom / valeur,
  • une nouvelle definition remplace la précédente mais ne l'efface pas, (on ne met pas une valeur à jours)

Définitions

L'utilisateur peut rajouter des définitions dans l'environnement grâce au mot clé let

  • let nom = expr permet de rajouter une définition simple dans l'environnement. Exemple : let n = 42
  • let nom1 = expr1 and nom2 = expr2 permet de rajouter plusieurs définitions simultanément. Exemple : let a=1 and b=2
  • let rec nom1 = expr1 and nom2 = expr2 permet de rajouter des définitions mutuellement récursives.

On peut toujours annoter la définition de types de données. Ceci évite à Caml d'avoir à deviner le type que l'on souhaite et permet de préciser la fonction : <source lang="ocaml">

let n:int = 42
let rec f (n:int) : string = if (n<1) then "" else "*" ^ (f (n-1))

</source>

Si une définition utilise le même nom qu'une définition précédente, la nouvelle définition écrase l'ancienne. Par exemple : <source lang="ocaml">

# let n=42 ;;
val n : int = 42
# let n=3.1415 ;;
val n : float = 3.1415
# n ;;
  • : float = 3.1415

</source>

Définitions locales

Il est possible de modifier l'environnement de manière temporaire (locale). On utilise alors let nom = expr1 in expr2. Ceci ajoute la définition nom = expr1 dans l'environnement, mais seulement pour l'évaluation de l'expression expr2. <source lang="ocaml">

  1. let x =
 let y = 42 in
   y/2 ;;

val x : int = 21

  1. x ;;
  • : int = 21
  1. y ;;

Unbound value y </source>

Les fonctions

Caml utilise la notation mathématique pour écrire les types des fonctions. Si f est une fonction avec un seul argument de type string et dont la valeur de retour est de type int (la fonction length par exemple), le type de f est noté string -> int. Dans l'interprète Caml : <source lang="ocaml">

# String.length ;;
  • : string -> int = <fun>

</source> Nous indique que la fonction length est bien de type string->int.

Une fonction est définie comme une variable avec un let et en utilisant le mot clé fun : <source lang="ocaml">

  1. let f = fun n -> n+1;;

val f : int -> int = <fun>

  1. let racine = fun n -> int_of_float (sqrt (float_of_int n));;

val racine : int -> int = <fun> </source>

Une fonction avec plusieurs argument est définie de la même manière avec <source lang="ocaml">

  1. let moyenne3 = fun x y z -> (x +. y +. z) /. 3. ;;

val moyenne3 : float -> float -> float -> float = <fun> </source>

Le type d'une fonction à plusieurs argument est noté en mettant plusieurs types à gauche de la flèche ->, comme ci dessus : type_arg1 -> type_arg2 -> type_arg3 -> type_resultat.

Remarques :

  • notez l'absence de mot clé return et l'absence de virgules pour séparer les différents arguments !
  • Le mot clé fun permet de définir des fonctions sans leur donner de nom, ce qui n'est pas possible dans les langages comme C ou Java...

On peut également utiliser un raccourci pour définir une fonction sans utiliser le mot clé fun : il suffit de mettre les arguments de la fonction avant le signe égal : <source lang="ocaml">

  1. let f n = n+1;;

val f : int -> int = <fun>

  1. let racine n = int_of_float (sqrt (float_of_int n));;

val racine : int -> int = <fun>

  1. let moyenne3 x y z = (x +. y +. z) /. 3. ;;

val moyenne3 : float -> float -> float -> float = <fun> </source>

Pour appliquer une fonction à des arguments, on écrit simplement <source lang="ocaml"> moyenne3 13.5 17.0 -0.333 </source> Il n'y a ni parenthèse, ni virgule. Les parenthèses peuvent cependant être nécessaires pour différencier <source lang="ocaml"> moyenne3 13.5 17.0 (-0.333 *. 6.) </source> et <source lang="ocaml"> (moyenne3 13.5 17.0 -0.333) *. 6. </source> ou pour que Caml lise correctement <source lang="ocaml"> moyenne3 13.5 (17.0 -. 12.0) -0.333 </source>

Fonctions récursives

Si on essaie de définir une fonction récursivement : <source lang="ocaml"> let f n = if (n<1) then 0 else n + f (n-1) </source> Caml répond <source lang="ocaml"> Error: Unbound value f </source> car f n'est pas présente dans l'environnement.

Pour rajouter f comme variable active (sans valeur) et pouvoir faire une définition récursive, il faut utiliser le mot clé rec : <source lang="ocaml">

  1. let rec f n = if (n<1) then 0 else n + f (n-1);;

val f : int -> int = <fun> </source>

Les définitions locales peuvent elles aussi être récursives (let rec ... in ...) ou mutuellement récursives (let rec ... and ... in ...). Elles acceptent également les annotations de type...

fonctions d'ordre supérieur

TODO

Les listes

Le type des listes est fondamental en programmation fonctionnelle. D'une certaine manière, on peut dire qu'ils remplacent les tableaux que l'on utilise constamment en programmation impérative.

Exercice : quels sont les différences importantes (utilisation, complexité, mémoire, ...) entre les tableaux et les listes ?

En Caml, une liste est représentée entre crochets, et les éléments sont séparés par des points-virgules : <source lang="ocaml">

# let une_liste = [ 1 ; 2 ; 2 ; -1 ; 0 ] ;;
val une_liste : int list = [1; 2; 2; -1; 0]
# let une_autre_liste = [ "coucou" ; "je m'appelle" ; "Bob" ] ;;
val une_autre_liste : string list = ["coucou"; "je m'appelle"; "Bob"]

</source>

Comme vous pouvez le voir dans l'exemple au dessus, le type des listes d'entiers s'appelle "int list", le type des listes de flottants s'appelle "float list" et le type des listes de listes d'entiers s'appelle ... "int list list".

Contrairement au cas des tuples, les éléments d'une liste doivent tous avoir le même type : <source lang="ocaml">

# let une_non_liste = [ 1 ; "Toto" ; 1.3 ] ;;
This expression has type string but is here used with type int

</source>

Attention : quel est le type de la liste suivante ? <source lang="ocaml">

# let une_liste_bizarre = [ 1 , 2 , 3 ] ;;

</source>

On peut ajouter un élement devant une liste avec l'opérateur "::" et la concaténation des listes s'obtient avec l'opérateur "@". <source lang="ocaml">

  1. 5::[1;2;3;4] ;;

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

  1. [-1;-2;-3;-4] @ [1;2;3;4] ;;

- : int list = [-1; -2; -3; -4; 1; 2; 3; 4] </source> Attention : la complexité de l'opération "::" ne dépend pas de la taille des listes mises en jeux. Par contre, la complexité de l'opération "@" dépend proportionnellement de la taille de son premier argument. Il faut donc toujour privilégier "::" par rapport à "@". En particulier, il est en général mauvais de rajouter des élément en fin de liste en utilisant "l @ [e]".

Filtrage, première partie : tuples et listes

La notion de filtrage, aussi appelé "pattern-matching" est un outils clé pour la programmation en Caml. L'idée est de décomposer une valeur en "sous-valeurs".

Filtrage sur les tuples

Comme une paire est de la forme (x,y), on peut décomposer une valeur de type a*b en deux sous valeurs. Voici un exemple de syntaxe : le code de la fonction fst : <source lang="ocaml">

 let premier p = match p with
   (x,y) -> x

</source>

Pour évaluer "premier e", Caml regarde le premier argument e et essaie de faire coïncider "(x,y)". S'il y arrive, alors il renvoie la valeur "x". Le typage de Caml inférera automatique que "p" doit être une paire, et il n'y a donc pas besoin de prévoir un cas par defaut.

On peut maintenant récupérer la deuxième composante d'un triplet de la manière suivante : <source lang="ocaml">

 let second3 t = match t with
   (_, y, _) -> y

</source> Notez l'utilisation de "_" pour donner la position d'une sous-valeur qui ne sert à rien.

Toutes les variables présentes dans le motif (la partie à gauche de la flèche) se retrouvent dans l'environnement avec pour valeur, la sous-valeur appropriée. Attention, ces variables ne se retrouvent dans l'environnement que dans la partie droite de la flèche. Ces variables remplacent (temporairement) les variables de même nom qui pouvaient se trouver dans l'environnement. Par exemple, la fonction : <source lang="ocaml">

 let f x = match x with
  (x,_) -> x

</source> est exactement la fonction fst.

Filtrage sur les listes

On peut décomposer une liste en deux sous-valeurs :

  • le premier élément de la liste,
  • la suite de la liste.

Autrement dit, si la liste l n'est pas vide, on peut la décomposer en e::q. Comme il est possible que la liste l soit vide, il y a en fait deux manière de décomposer l :

  • soit [] (sans aucune sous-valeur),
  • soit e::q (avec deux sous-valeurs).

Par exemple, voici une manière de tester si une liste est vide : <source lang="ocaml">

let vide l = match l with
    [] -> true
  | x::xs  ->  false

</source> Les différents cas sont séparés par le symbole "|".

On peut également décomposer une liste en 3 sous-valeurs : le premier élément, le second élément et la suite de la liste. Dans ce cas, il a trois cas pour une liste arbitraire :

  • elle est vide [] (aucune sous-valeur),
  • elle ne contient qu'une seul élément [e] (une sous-valeur),
  • elle contient un premier élément, un deuxième élément, et une suite a::b::q (trois sous-valeurs).

On peut par exemple écrire : <source lang="ocaml"> let deux_elements_ou_plus l = match l with

   [] -> false
 | [a] -> false
 | a::b::q -> true

</source>

Comme on peut toujours décomposer une valeur en une sous-valeur (la valeur elle même), on peut toujours avoir un motif de la forme x. Toutes les valeurs pourront être décomposées de cette manière, et il faut donc que ce motif soit le dernier. (Sinon, les motifs suivants ne servent à rien...) On parle de motif universel. (On peut utiliser _ comme motif universel.) Par exemple, on peut réecrire l'exemple précédent : <source lang="ocaml"> let deux_elements_ou_plus

   _::_::_  -> true
 | _ -> false

</source>

Compléments

  1. On peut ajouter des constantes dans les motifs. Le motif ne sera utilisé que lorsque la sous-valeur correspondantes a la valeurs correspondante. Par exemple, si on modélise les entiers de Gauss (« entiers complexes ») par des paires d'entiers, on peut définir :

<source lang="ocaml">

 let imaginaire_pur c = match c with
   (0,0) -> false
 | (0,_) -> true
 | _ -> false

</source>

  1. On peut grouper des motifs différents pour faire la même chose. Il faut que les variables qui apparaissent dans les motifs soient les mêmes, et qu'elles aient le même type :

<source lang="ocaml"> let f x = match x with

   0::l | 1::_::l | 2::_::_::l  -> l  (* on groupe 3 motifs en une seule ligne *)
 | [] -> []
 | x::_ -> [x]

</source>

  1. on peut « garder » les motifs" par une condition avec le mot clé "when"

<source lang="ocaml"> let g x = match x with

   x::xs  when (List.lenght (f xs) = 1)  ->  true
 | _ -> false

</source> Attention dans le cas où vous utilisez when : il est conseillé de terminer votre filtrage avec un motif universel pour être sûr que vous n'avez pas oublié de cas.

  1. comme on définit souvent des fonctions par filtrage, Caml autorise la syntaxe

<source lang="ocaml"> function

   pattern1 -> expr1
 | pattern2 -> expr2
 | ...

</source> plutôt que <source lang="ocaml"> fun x -> match x with

   pattern1 -> expr1
 | pattern2 -> expr2
 | ...

</source> Remarquez que l'on ne peut faire ça que pour une fonction à une seule variable...

  1. fonction par filtrage : s'il n'y a qu'un seul motif, on peut alors utiliser le mot clé "fun" habituel, ce qui permet la définition d'une fonction à plusieurs arguments. (Mais dans ce cas, les parenthèses autour des tuples sont obligatoires.)

<source lang="ocaml">

  1. let h = fun (x,y) z (u,v,w) -> x+y+z+v ;;

val h : int * int -> int -> 'a * int * 'b -> int = <fun> </source>

Le polymorphisme

Transparence référentielle

Les types algébriques

On peut défnir un synonyme de type avec le mot clef type Exemple : <source lang="ocaml">type point = float*float</source>

Types énumérés

On donne tous les éléments du type Exemple : <source lang="ocaml">type jours = Lundi | Mardi | Mercredi | ...</source>

Type avec 7 éléments. Les éléments commencent forcément par une majuscule.

On peut comparer des éléments avec "=", "<" et ">" (< et > sont à éviter, il vaut mieux écrire une fonction pour comparer) On peut aussi faire du filtrage.

Exemple: <source lang="ocaml"> let week_end j = match j with

    Samedi -> true
  | Dimanche -> true
  | _ -> false

</source>

Construction de types paramètres

Exemple : type des transformations de l'espace avec : - Translation - Rotation - Symetrie On aura alors des constructeurs avec des paramètres :

<source lang="ocaml"> type transformation = Rotation of int*point

                   | Translation of point
                   | SymCentrale of point
                   | Homothetie of float*point

</source>

On n'a pas donné explicitement tous les éléments, mais on les a donné implicitement. Exemple : <source lang="ocaml"> let rien = Translation (0.0,0.0) </source> Autre exemple : <source lang="ocaml"> let fait quelquechose t = match t with

    Rotation(a, (x, y) −> a mod 360 != 0
  | Translation(x, y) −> x != 0.0 || y != 0.0
  | SymCentrale(x, y) −> true
  | Homothetie(x, (−, −)) −> r != 1.0

</source>

Les types récursifs

En Caml <source lang="ocaml"> type node = Node of int*node | Vide </source> exemple d'éléments de "node" : - Vide - Node(0,Vide) - Node(1,Node(2,Node(3, Vide)))

Le type "node" est un type récursif. Comme les fonctions récursivent doivent avoir des cas de base, les types récursifs doivent avoir des constructeurs "de base" (non récursifs). On peut comparer des éléments avec "=". On peut aussi utiliser le filtrage.

Exemple : <source lang="ocaml"> let rec taille l = match l with Vide -> 0 |Node(_,l)->1+taille l </source>

La fonction taille sur les listes Caml suit le même schéma avec les abréviations Caml. <source lang="ocaml"> let rec taille l = match l with []->0 | a ::l->1+taille </source> Ceci explique la différence entre :: (constructeur) et @ (fonction récursive)

Exemple de type récursif, les arbres binaires : <source lang="ocaml"> type bin = Vide | Noeud of int*bin*bin </source> arbre naire : <source lang="ocaml"> type arbre n_aire = Vide|Node of int ∗ (arbre_naire list)(bancal) </source>

Les structures

La récursivité terminale

Les exceptions

Aspects impératifs dans Caml

Les modules