Tu arrives en MP2I et tu découvres qu'en cours d'informatique, on ne programme plus en Python mais en OCaml, un langage dont beaucoup n'ont jamais entendu parler avant la prépa. Première réaction fréquente : « pourquoi apprendre un langage que personne n'utilise au lycée ? » La réponse tient en une phrase : OCaml est fait pour raisonner juste, et c'est exactement ce que les concours d'informatique évaluent.
Ce guide te donne la prise en main complète d'OCaml pour l'informatique de MP2I et sa suite en MPI : pourquoi la programmation fonctionnelle et le typage fort, les valeurs et les types avec l'inférence, let et let ... in, les fonctions et la récursivité avec let rec, le filtrage par motif, les listes et les n-uplets, une comparaison avec Python, des exemples de code commentés et trois exercices corrigés. Nos profs Hadamard, anciens de MP2I et de MPSI option info passés par l'X, l'ENS et CentraleSupélec, ont tous codé en OCaml aux concours : ils t'évitent les pièges du démarrage.
Pourquoi OCaml en MP2I et MPI
En MP2I puis MPI, l'informatique n'est pas une option mais une discipline principale, et son programme repose sur deux langages : OCaml pour la programmation fonctionnelle et le langage C pour la programmation impérative et système. Cet article se concentre sur OCaml, le langage fonctionnel, qui est aussi celui des épreuves d'informatique aux concours (X-ENS, Mines-Ponts, Centrale-Supélec). Deux traits le définissent, et ce sont précisément les deux compétences que le programme cherche à installer.
Premier trait : c'est un langage fonctionnel. Là où Python empile des instructions qui modifient des variables, OCaml se construit à partir de fonctions qui prennent des valeurs et en renvoient d'autres. On ne dit pas « fais ceci, puis change cela », on dit « le résultat est cette expression ». Cette manière de penser colle à la récursivité et à la preuve de correction, deux piliers du programme d'informatique de prépa.
Second trait : le typage fort. Chaque valeur a un type (un entier, un flottant, une chaîne, une liste d'entiers) et le compilateur vérifie que tout est cohérent avant d'exécuter la moindre ligne. Un programme qui tente d'additionner un entier et une chaîne ne compile pas — il est rejeté à la source. On perd la liberté de Python, on gagne un filet de sécurité qui attrape énormément de bugs sans même lancer le code.
L'idée à retenir. OCaml n'est pas là pour faire « comme au lycée ». Il est là pour t'obliger à écrire du code dont la logique est claire et vérifiée. Le typage fort et la pensée fonctionnelle sont un investissement : au début ça freine, très vite ça fait gagner un temps précieux en debug.
Valeurs, types et inférence
En OCaml, tout est une valeur, et toute valeur possède un type. Les types de base que tu manipuleras dès les premières séances sont au nombre de cinq :
| Type | Ce qu'il représente | Exemple de valeur |
|---|---|---|
int | Entier | 42 |
float | Nombre à virgule flottante | 3.14 |
bool | Booléen | true, false |
char | Caractère unique | 'a' |
string | Chaîne de caractères | "prépa" |
Le point qui surprend au début : tu n'écris presque jamais les types toi-même. OCaml les devine grâce à l'inférence de type. Tu déclares une valeur, le compilateur en déduit le type et te le renvoie :
let x = 42 (* OCaml infère : x est un int *)
let pi = 3.14 (* OCaml infère : pi est un float *)
let ok = true (* OCaml infère : ok est un bool *)
let lettre = 'a' (* OCaml infère : lettre est un char *)
let mot = "prépa" (* OCaml infère : mot est un string *)
Piège de démarrage numéro un : les entiers et les flottants ne partagent pas leurs opérateurs. On additionne deux int avec + et deux float avec +. (idem *., -., /.). Écrire 3.0 + 1.0 est une erreur de type ; il faut 3.0 +. 1.0. OCaml ne convertit rien tout seul, c'est voulu.
let et let ... in : nommer des valeurs
Le mot-clé let sert à donner un nom à une valeur. Employé seul, il crée une définition globale, visible dans tout le reste du programme :
let rayon = 5 (* rayon vaut 5, visible partout ensuite *)
let diametre = 2 * rayon (* diametre vaut 10 *)
La variante let ... in crée au contraire une définition locale : le nom n'existe que dans l'expression placée après le in, puis disparaît. C'est le bon outil pour nommer des résultats intermédiaires à l'intérieur d'un calcul, sans polluer l'espace global :
let aire_rectangle =
let largeur = 5 in (* largeur n'existe que dans les lignes suivantes *)
let hauteur = 3 in (* hauteur non plus *)
largeur * hauteur (* valeur renvoyée : 15 *)
(* ici, largeur et hauteur n'existent plus *)
Attention à une différence de vocabulaire avec Python : en OCaml, un let ne se « réaffecte » pas. On ne modifie pas la valeur d'un nom déjà défini, on en introduit un nouveau. Cette immuabilité est au cœur de la pensée fonctionnelle et rend le code bien plus facile à suivre pas à pas.
Cours particuliers
Maîtrisez l'informatique avec un prof expert
Python, algorithmique, structures de données... Un accompagnement adapté.
Fonctions et récursivité avec let rec
Une fonction se définit avec fun, ou plus couramment avec la forme raccourcie où l'on écrit les arguments juste après le nom. Les deux définitions ci-dessous sont strictement équivalentes :
let double = fun x -> 2 * x (* forme explicite avec fun *)
let double x = 2 * x (* forme raccourcie, la plus utilisée *)
L'application d'une fonction se fait par simple juxtaposition, sans parenthèses autour de l'argument : on écrit double 21 et non double(21). Le résultat vaut 42. Une fonction à plusieurs arguments les liste séparés par des espaces :
let somme_deux a b = a + b (* deux arguments *)
let resultat = somme_deux 3 4 (* resultat vaut 7 *)
Pour qu'une fonction puisse s'appeler elle-même, il faut le déclarer explicitement avec let rec (« rec » pour récursive). C'est le mot-clé le plus emblématique de l'informatique de prépa. Voici l'exemple canonique, la factorielle :
let rec factorielle n =
if n = 0 then 1 (* cas de base : 0! = 1 *)
else n * factorielle (n - 1) (* cas récursif : n! = n * (n-1)! *)
(* factorielle 5 vaut 5 * 4 * 3 * 2 * 1 = 120 *)
On y lit les deux ingrédients de toute fonction récursive : un cas de base qui arrête la descente (ici n = 0) et un cas récursif qui rappelle la fonction sur un problème plus petit (ici n - 1). Sans cas de base, l'appel ne s'arrête jamais. La récursivité mérite un article à elle seule, on la détaille dans notre guide dédié à la récursivité en prépa avec exemples.
Le filtrage par motif : match ... with
Le filtrage par motif compare une valeur à une série de formes possibles et exécute le premier cas qui correspond. C'est l'outil le plus puissant du langage, celui qui remplace les longues cascades de if ... else. La syntaxe se lit très naturellement :
let jour_texte n =
match n with
| 0 -> "dimanche"
| 1 -> "lundi"
| 2 -> "mardi"
| _ -> "autre jour" (* _ est le motif « n'importe quoi d'autre » *)
Chaque ligne commence par une barre |, porte un motif à gauche de la flèche -> et le résultat à droite. Le motif spécial _ (le tiret bas) capture tous les cas restants : il joue le rôle du else final et évite d'oublier une valeur. Le vrai intérêt du filtrage apparaît avec les listes, qu'on décortique juste en dessous.
Trouvez le prof qu'il vous faut
Échangez avec notre équipe pour trouver le professeur idéal selon vos besoins.
Les listes et leur parcours récursif
Une liste OCaml regroupe des éléments de même type, entre crochets et séparés par des points-virgules (attention, pas des virgules). On construit une liste plus longue en ajoutant un élément en tête avec l'opérateur ::, appelé « cons » :
let entiers = [1; 2; 3; 4] (* une liste d'int : int list *)
let vide = [] (* la liste vide *)
let ajout = 0 :: entiers (* [0; 1; 2; 3; 4] : on ajoute en tête *)
Toute liste non vide se décompose en deux morceaux : sa tête (le premier élément) et sa queue (la liste des éléments restants). Cette structure « tête + reste » se marie parfaitement avec le filtrage par motif, ce qui donne le schéma universel du parcours de liste : un cas pour [], un cas pour tete :: reste. Exemple, la longueur d'une liste :
let rec longueur liste =
match liste with
| [] -> 0 (* liste vide : longueur nulle *)
| _ :: reste -> 1 + longueur reste
(* on ignore la tête (_), on compte 1 + longueur du reste *)
(* longueur [10; 20; 30] vaut 3 *)
Même schéma pour la somme des éléments d'une liste d'entiers, à ceci près qu'on a cette fois besoin de la tête pour l'additionner :
let rec somme liste =
match liste with
| [] -> 0 (* somme d'une liste vide = 0 *)
| tete :: reste -> tete + somme reste
(* on ajoute la tête à la somme du reste *)
(* somme [10; 20; 30] vaut 60 *)
Le réflexe à ancrer. Pour parcourir une liste, on ne fait pas de boucle « pour i de 0 à n » comme en Python : on écrit un match à deux cas, [] et tete :: reste, et on rappelle la fonction sur reste. Une fois ce moule intégré, tu écris longueur, somme, maximum, appartenance, renversement… toujours de la même façon. Les listes ouvrent la porte aux structures de données de prépa (piles, files, arbres).
Les n-uplets
Un n-uplet (couple, triplet…) regroupe plusieurs valeurs, éventuellement de types différents, séparées par des virgules. Contrairement à une liste, sa taille est fixe et ses composantes peuvent mélanger les types :
let point = (3, 5) (* un couple : int * int *)
let eleve = ("Marie", 17, true) (* un triplet : string * int * bool *)
Le type d'un couple d'entiers se note int * int, celui du triplet string * int * bool. Pour récupérer les composantes, on utilise à nouveau le filtrage, cette fois directement dans un let :
let (x, y) = point (* x vaut 3, y vaut 5 *)
(* une fonction qui échange les deux composantes d'un couple *)
let echange (a, b) = (b, a)
(* echange (3, 5) vaut (5, 3) *)
Les n-uplets sont parfaits pour qu'une fonction renvoie plusieurs résultats à la fois — un quotient et un reste, un minimum et un maximum — sans avoir à créer de structure dédiée.
OCaml face à Python : ce qui change
Si tu viens de la spé NSI ou de l'informatique de tronc commun, tu connais Python. Voici les différences qui comptent pour ne pas transposer de mauvais réflexes. Elles expliquent aussi pourquoi la prépa MP2I a choisi OCaml, un choix qu'on détaille dans notre avis sur la filière MP2I après la NSI.
| Aspect | Python | OCaml |
|---|---|---|
| Paradigme dominant | Impératif (boucles, affectations) | Fonctionnel (fonctions, récursivité) |
| Typage | Dynamique, vérifié à l'exécution | Fort et statique, vérifié à la compilation |
| Déclaration de type | Facultative | Inutile : inférée automatiquement |
| Entiers et flottants | Mêmes opérateurs, conversion implicite | + pour les int, +. pour les float |
| Parcours d'une collection | Boucle for / while | Filtrage par motif et récursivité |
| Sélection multi-cas | Cascade de if / elif / else | match ... with |
Le vrai basculement est mental. En Python tu décris une suite d'actions ; en OCaml tu décris ce qu'est le résultat. Une fois ce déclic passé, le typage fort cesse d'être une contrainte et devient un correcteur automatique qui refuse tes erreurs avant qu'elles ne coûtent une exécution.
Trois exercices corrigés
À toi de jouer. Cache la correction, écris ta version, puis compare. Les trois exercices reprennent le moule « cas de base + cas récursif » avec un filtrage par motif.
Exercice 1, la puissance
Énoncé. Écrire une fonction récursive puissance qui, à partir d'un entier x et d'un entier n positif ou nul, calcule x élevé à la puissance n.
Solution. Le cas de base est n = 0 (toute puissance nulle vaut 1), le cas récursif utilise l'identité qui dit que x puissance n égale x fois x puissance n - 1.
let rec puissance x n =
if n = 0 then 1
else x * puissance x (n - 1)
(* puissance 2 10 vaut 1024 *)
Exercice 2, le maximum d'une liste
Énoncé. Écrire une fonction récursive maximum qui renvoie le plus grand élément d'une liste d'entiers non vide.
Solution. On distingue trois motifs : la liste vide (cas interdit, on lève une erreur), la liste à un seul élément (c'est lui le maximum) et la liste plus longue (on compare la tête au maximum du reste).
let rec maximum liste =
match liste with
| [] -> failwith "liste vide" (* cas interdit *)
| [x] -> x (* un seul élément : c'est le max *)
| tete :: reste ->
let m = maximum reste in (* maximum du reste, nommé localement *)
if tete > m then tete else m
(* maximum [3; 9; 2; 7] vaut 9 *)
On note l'usage de let ... in pour ne calculer maximum reste qu'une seule fois et le réutiliser dans la comparaison — plus efficace que de l'appeler deux fois.
Exercice 3, l'appartenance
Énoncé. Écrire une fonction récursive appartient qui indique (par un booléen) si un entier x figure dans une liste d'entiers.
Solution. La liste vide ne contient rien (false). Sinon, x appartient à la liste s'il est égal à la tête, ou bien s'il appartient au reste — un « ou » logique noté ||.
let rec appartient x liste =
match liste with
| [] -> false
| tete :: reste -> tete = x || appartient x reste
(* appartient 7 [3; 9; 2; 7] vaut true *)
(* appartient 5 [3; 9; 2; 7] vaut false *)
Les trois solutions partagent la même colonne vertébrale : un motif d'arrêt, un motif récursif, et un appel sur une donnée plus petite. C'est le squelette de la quasi-totalité des fonctions que tu écriras cette année en OCaml.
Ce qu'il faut retenir
- Pourquoi OCaml : langage fonctionnel à typage fort, taillé pour la récursivité et la preuve de correction, utilisé aux concours d'informatique.
- Types et inférence :
int,float,bool,char,string; le compilateur devine les types tout seul et rejette le code incohérent avant l'exécution. - let et let ... in :
letnomme une valeur globale,let ... inune valeur locale à une expression. - Fonctions et let rec : application sans parenthèses, et
let recpour les fonctions récursives (cas de base + cas récursif). - Filtrage par motif :
match ... withremplace les cascades deifet décompose naturellement les listes en[]ettete :: reste. - Listes et n-uplets : les listes se parcourent par récursivité ; les n-uplets regroupent des valeurs de types différents et servent à renvoyer plusieurs résultats.
La prise en main d'OCaml est un cap qui déroute une ou deux semaines, puis devient une seconde nature. Le programme d'informatique de prépa s'appuie ensuite sur ces bases pour aborder la récursivité avancée et les structures de données. Si tu hésites encore sur l'intérêt de l'option, notre article sur l'option informatique en MPSI t'aidera à décider en connaissance de cause.


