Syntaxe et sémantique ; théorème de Kleene, expressions régulières ↔ automates finis ; construction de Thompson et élimination d'états.
Vous connaissez déjà les expressions régulières sans le savoir : c'est la syntaxe \d{2}/\d{2}
qui valide une date dans un formulaire, [a-z]+@[a-z]+\.[a-z]+ qui filtre une adresse de
courriel, le motif tapé dans la recherche d'un éditeur de texte. Ce chapitre montre ce qu'elles
sont vraiment — une troisième façon de décrire un langage régulier, purement textuelle — et
révèle le résultat qui unifie tout le bloc II : le théorème de Kleene, qui affirme
qu'expressions régulières et automates finis décrivent exactement les mêmes langages.
Trois descriptions, un seul monde. C'est l'un des plus beaux résultats du cours, et le plus utile en pratique.
Syntaxe : trois opérations et rien d'autre
Une expression régulière sur un alphabet se construit inductivement (chapitre 2), à partir de briques élémentaires et de trois opérations. Les briques :
- dénote le langage vide ;
- dénote le langage ;
- une lettre dénote .
Les opérations, appliquées à des expressions déjà construites :
| Écriture | Nom | Priorité |
|---|---|---|
| union (alternative) | la plus faible | |
| ou | concaténation | intermédiaire |
| étoile de Kleene | la plus forte |
La priorité se lit comme en arithmétique : l'étoile est le « puissance », la concaténation le « produit », l'union la « somme ». Ainsi se lit , et les parenthèses servent à forcer un autre regroupement, comme dans .
Ce sont exactement les trois opérations sur les langages du chapitre 1. Une expression régulière n'est rien d'autre qu'une notation compacte pour les combiner — pas un mécanisme nouveau.
Sémantique : le langage dénoté
À chaque expression correspond un langage , défini en suivant sa construction :
Lisons quelques exemples sur — cette lecture est le savoir-faire du chapitre :
- dénote — un seul mot ;
- dénote — « ou » ;
- dénote — noter que en fait partie ;
- dénote tous les mots sur , c'est-à-dire ;
- dénote « tous les mots qui se terminent par » — le langage même des chapitres 3 et 4, décrit ici en cinq symboles.
Ce dernier exemple mérite qu'on s'y arrête : ce que l'AFD du chapitre 3 exprimait par trois états et six transitions tient en une courte expression. C'est la force de la notation — mais rappelez-vous qu'elle décrit exactement le même langage, ni plus ni moins.
Deux pièges classiques, tous deux liés au chapitre 1 :
- L'étoile inclut toujours le mot vide. contient même si — la concaténation de « zéro copie ». Oublier ce cas fausse la moitié des exercices.
- et ne sont pas la même chose. , tandis que tout court dénote le langage vide. C'est la distinction du chapitre 1, qui revient à l'identique.
Que dénote l'expression régulière (a|b)*a sur Σ = {a, b} ?
Le théorème de Kleene
Voici le résultat qui referme le bloc et justifie tout ce qui précède :
Théorème de Kleene. Un langage est décrit par une expression régulière si et seulement si il est reconnu par un automate fini.
Les trois descriptions — AFD, AFN, expression régulière — sont donc équivalentes : elles définissent une seule et même classe, les langages réguliers. C'est pourquoi on peut passer librement de l'une à l'autre selon ce qui est commode : écrire une expression régulière (concis), la transformer en automate pour l'exécuter efficacement (rapide), raisonner sur l'automate pour prouver une propriété.
La preuve se fait dans les deux sens, et chaque sens est une construction algorithmique — pas seulement une existence. C'est ce qui la rend utile : elle donne les recettes qu'emploient les outils réels.
D'une expression à un automate : Thompson
Le sens « expression → automate » emploie la construction de Thompson. Elle procède inductivement, exactement selon la structure de l'expression, en assemblant de petits AFN avec des ε-transitions (chapitre 4) — dont c'est ici l'usage principal :
- une lettre : deux états reliés par une flèche ;
- une union : un nouvel état initial pointe par vers les automates de et de (les deux branches de l'alternative) ;
- une concaténation : la sortie de l'automate de est branchée par sur l'entrée de celui de ;
- une étoile : on ajoute des ε-transitions pour pouvoir répéter l'automate de ou le sauter (le cas « zéro copie »).
Le résultat est un AFN avec ε-transitions, qu'on rend déterministe par la construction du chapitre 4 si l'on veut l'exécuter. C'est très exactement la chaîne qu'un moteur d'expressions régulières parcourt en interne.
D'un automate à une expression : élimination d'états
Le sens inverse, « automate → expression », emploie l'élimination d'états. On retire les états un par un ; à chaque retrait, on reporte l'information perdue sur les transitions restantes, en les étiquetant non plus par des lettres mais par des expressions régulières. À la fin, il ne reste qu'une transition de l'entrée à la sortie, étiquetée par l'expression régulière cherchée.
Vous n'avez pas à mémoriser les détails de ces deux constructions en L2 ; vous devez retenir qu'elles existent, qu'elles sont effectives (on peut les programmer), et qu'ensemble elles prouvent le théorème de Kleene par double construction.
Un ingénieur écrit une expression régulière pour valider un format de saisie, puis a besoin de la vérifier des millions de fois par seconde sur un flux. Que permet le théorème de Kleene dans ce contexte ?
À vous
L'exercice donne aux expressions régulières leur sens en code. Vous implémentez la sémantique — chaque opérateur (union, concaténation, étoile) est l'opération correspondante sur les langages du chapitre 1 — puis vous calculez le langage de et vérifiez quels mots il contient.
L'étoile vous forcera à traiter le cas « zéro copie » : oublier le mot vide initial rend l'opération fausse. Et vous devrez borner par une longueur, parce que le langage est infini — une petite expression décrit un langage sans fin, tout comme un automate avec ses boucles. C'est là, concrètement, le théorème de Kleene.
Implémentez la sémantique d'une expression régulière : chaque opérateur (union, concaténation, étoile) est une opération sur les langages du chapitre 1. Calculez le langage de (a|b)*ab et vérifiez quels mots il contient — l'étoile vous rappellera le piège du « zéro copie ».
// Une expression régulière décrit un LANGAGE. Ici on la représente par un // arbre, et on calcule le langage qu'elle dénote — jusqu'à une longueur max, // car l'étoile produit un ensemble infini. // // Opérateurs (rappel chapitre 1) : // lettre -> { "a" } vide -> {} epsilon -> { "" } // union(E,F) = L(E) ∪ L(F) // concat(E,F) = L(E) · L(F) (tous les u·v, u∈L(E), v∈L(F)) // etoile(E) = L(E)* (zéro, un ou plusieurs mots de L(E)) // Constructeurs d'arbre : const lettre = (c) => ({ type: "lettre", c }); const epsilon = { type: "epsilon" }; const vide = { type: "vide" }; const union = (g, d) => ({ type: "union", g, d }); const concat = (g, d) => ({ type: "concat", g, d }); const etoile = (e) => ({ type: "etoile", e }); // ── À VOUS : la sémantique ────────────────────────────────────────────────── // langage(expr, max) rend l'ENSEMBLE (Set) des mots de longueur <= max décrits // par expr. function langage(expr, max) { switch (expr.type) { case "vide": return new Set(); // aucun mot case "epsilon": return new Set([""]); // le seul mot vide case "lettre": return new Set([expr.c]); // un seul mot d'une lettre case "union": { // à compléter : réunir les deux langages return new Set(); } case "concat": { // à compléter : tous les u+v (u du gauche, v du droit), longueur <= max return new Set(); } case "etoile": { // à compléter : ε, puis L, puis L·L, ... tant que la longueur <= max return new Set(); } } } // ── L'expression (a|b)*ab : « se termine par ab » ───────────────────────── const expr = concat(etoile(union(lettre("a"), lettre("b"))), concat(lettre("a"), lettre("b"))); const mots = [...langage(expr, 3)].sort((x, y) => x.length - y.length || x.localeCompare(y)); console.log("(a|b)*ab, mots de longueur <= 3 :"); console.log(" [" + mots.join(", ") + "]"); console.log(" ab present ?", mots.includes("ab")); // attendu true console.log(" aab present ?", mots.includes("aab")); // attendu true console.log(" ba present ?", mots.includes("ba")); // attendu false
Ce que la suite en fait
Le bloc II tient debout : trois descriptions équivalentes des langages réguliers, reliées par des constructions effectives. Une question reste ouverte, et c'est la plus importante — tous les langages sont-ils réguliers ?
Le chapitre 6 y répond, et la réponse est non. Il donne d'abord l'outil pour ranger un automate (la minimisation), recense ce que la classe régulière sait faire (les propriétés de clôture), puis l'outil qui prouve qu'un langage échappe aux automates : le lemme de pompage, second point qui coince de l'année, avec son exemple canonique — celui-là même que l'exercice du chapitre 1 avait déjà mis de côté.
À retenir
Exercices d'entraînement
Écrire des expressions régulières
Sur , donner une expression régulière pour chacun de ces langages :
- les mots contenant au moins un
a; - les mots de longueur paire ;
- les mots ne contenant pas le facteur
aa.
Décrire un langage
Décrire en français le langage dénoté par chacune de ces expressions, sur :
- ;
- ;
- .
Du théorème de Kleene
Donner une expression régulière décrivant le langage reconnu par l'AFD « mots se terminant par a » (croisé au chapitre 3). Que garantit le théorème de Kleene à propos d'une telle conversion ?
Vous avez parcouru les 9 sections.
Marquez-la terminée pour faire avancer votre parcours, ou revenez sur un point avant de passer à la suite.