140
Principes des langages de programmation INF 321 Eric Goubault 14 juin 2012

Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Principes des langages de programmation

INF 321

Eric Goubault

14 juin 2012

Page 2: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2

Page 3: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Table des matieres

1 Introduction 7

2 Programmation imperative 112.1 Variables et types . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.2 Codage des nombres . . . . . . . . . . . . . . . . . . . . . . . . . 132.3 Expressions arithmetiques . . . . . . . . . . . . . . . . . . . . . . 142.4 Instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142.5 Semantique elementaire . . . . . . . . . . . . . . . . . . . . . . . 142.6 Les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162.7 Semantique des references . . . . . . . . . . . . . . . . . . . . . . 172.8 La sequence d’instructions . . . . . . . . . . . . . . . . . . . . . . 182.9 Conditionnelles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192.10 Iteration - la boucle . . . . . . . . . . . . . . . . . . . . . . . . . 202.11 Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.12 Passage d’arguments aux fonctions . . . . . . . . . . . . . . . . . 212.13 Variables locales, variables globales . . . . . . . . . . . . . . . . . 242.14 References, pointeurs, objets . . . . . . . . . . . . . . . . . . . . 262.15 Un peu de syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . 32

3 Structures de donnees 353.1 Types produits, ou enregistrements . . . . . . . . . . . . . . . . . 353.2 Le ramasse-miette, ou GC . . . . . . . . . . . . . . . . . . . . . . 373.3 Enregistrements et appels de fonctions . . . . . . . . . . . . . . . 383.4 Les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393.5 Egalite physique et egalite structurelle . . . . . . . . . . . . . . . 423.6 Partage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433.7 Types somme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453.8 Types de donnees dynamiques . . . . . . . . . . . . . . . . . . . . 483.9 Les listes lineaires . . . . . . . . . . . . . . . . . . . . . . . . . . 503.10 Application aux tables de hachage... . . . . . . . . . . . . . . . . 513.11 Listes et partage . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

3

Page 4: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

4 TABLE DES MATIERES

4 Programmation orientee objet, en JAVA 574.1 Statique versus dynamique . . . . . . . . . . . . . . . . . . . . . 574.2 Types somme, revisites . . . . . . . . . . . . . . . . . . . . . . . . 604.3 Heritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614.4 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 634.5 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654.6 Heritage et typage . . . . . . . . . . . . . . . . . . . . . . . . . . 654.7 Classes abstraites . . . . . . . . . . . . . . . . . . . . . . . . . . . 664.8 Paquetages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674.9 Collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684.10 Les objets en (O)Caml . . . . . . . . . . . . . . . . . . . . . . . . 68

5 Recursivite, calculabilite et complexite 695.1 La recursivite dans les langages de programmation . . . . . . . . 695.2 Pile d’appel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705.3 Recurrence structurelle . . . . . . . . . . . . . . . . . . . . . . . . 745.4 Partage en memoire et recursivite . . . . . . . . . . . . . . . . . . 765.5 Les fonctions recursives primitives . . . . . . . . . . . . . . . . . 785.6 Fonctions recursives partielles . . . . . . . . . . . . . . . . . . . . 805.7 Pour aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . 815.8 Quelques elements en complexite . . . . . . . . . . . . . . . . . . 82

6 Semantique denotationnelle 896.1 Semantique elementaire . . . . . . . . . . . . . . . . . . . . . . . 896.2 Problemes de points fixes . . . . . . . . . . . . . . . . . . . . . . 916.3 Semantique de la boucle while . . . . . . . . . . . . . . . . . . . 956.4 Semantique des fonctions recursives . . . . . . . . . . . . . . . . . 966.5 Continuite et calculabilite . . . . . . . . . . . . . . . . . . . . . . 97

7 Logique, modeles et preuve 997.1 Syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 997.2 Semantique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1007.3 Decidabilite des formules logiques . . . . . . . . . . . . . . . . . . 1027.4 Pour aller plus loin... . . . . . . . . . . . . . . . . . . . . . . . . . 1037.5 Un peu de theorie de la demonstration . . . . . . . . . . . . . . . 103

8 Validation et preuve de programmes 1098.1 La validation, pour quoi faire ? . . . . . . . . . . . . . . . . . . . 1098.2 Preuve a la Hoare . . . . . . . . . . . . . . . . . . . . . . . . . . 111

9 Typage, et programmation fonctionnelle 1179.1 PCF (non type) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1179.2 Semantique operationnelle . . . . . . . . . . . . . . . . . . . . . . 1189.3 Ordres d’evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . 1219.4 Appel par nom, appel par valeur et appel par necessite . . . . . . 1219.5 Combinateurs de point fixe . . . . . . . . . . . . . . . . . . . . . 123

Page 5: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

TABLE DES MATIERES 5

9.6 Typage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1259.7 Theorie de la demonstration et typage . . . . . . . . . . . . . . . 1289.8 Pour aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . 131

10 Programmation reactive synchrone 13310.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13310.2 Lustre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13310.3 Calcul d’horloges . . . . . . . . . . . . . . . . . . . . . . . . . . . 13710.4 Pour aller plus loin... . . . . . . . . . . . . . . . . . . . . . . . . . 13810.5 Reseaux de Kahn et semantique de Lustre . . . . . . . . . . . . . 138

Page 6: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

6 TABLE DES MATIERES

Page 7: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Chapitre 1

Introduction

Objectif du cours Ce cours de L3 vise a donner des elements conceptuelsconcernant les langages de programmation. Il a ete concu pour etre lisible pardes eleves de classes preparatoires MPI essentiellement, qui ont deja l’experiencede la programmation en (O)Caml, et des connaissances en algorithmique ele-mentaire. On insiste dans ce cours sur les concepts nouveaux pour cette categoried’etudiants que sont les notions de calculabilite et complexite, et surtout sur lasemantique mathematique des langages de programmation. Il est illustre par uncertain nombre de paradigmes de programmation : la programmation impera-tive, la programmation fonctionnelle et la programmation reactive synchrone.On n’y traite pas, encore, de programmation logique par exemple.

Contenu du cours Le chapitre 2 met en place les principes des langagesimperatifs, et permet d’introduire doucement a la syntaxe Java. On en profitepour mettre en place certaines notations de semantique denotationnelle (qui seradeveloppee au chapitre 6) que l’on utilise pour clarifier la notion d’adresse (lo-cation memoire, et reference), souvent mal maıtrisee par les jeunes etudiants eninformatique, specialement quand ils n’ont qu’une experience en programmationfonctionnelle.

L’utilisation de structures de donnees complexes n’est introduite qu’au cha-pitre 3, avec une vision tres algebrique (somme, produit, equations recursivesaux domaines), proche de considerations que l’on peut avoir en semantique,et en particulier en typage de langages (fonctionnels en general). Cette visionpermettra la necessaire deformation de l’esprit permettant de bien assimiler lechapitre 9.

Le chapitre 4 traite du paradigme oriente objet, a travers Java principale-ment.

Le chapitre 5 demarre la partie plus theorique du cours : sous le pretexted’expliquer l’execution de fonctions recursives, on developpe quelques conceptselementaires de la calculabilite (fonctions recursives primitives, et fonctions re-cursives partielles), et l’on termine par quelques notions sur les classes de com-plexite. Ce chapitre introduit a des nombreux concepts developpes dans le cours

7

Page 8: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

8 CHAPITRE 1. INTRODUCTION

INF 423.

Le chapitre 6 developpe les outils classiques de la semantique denotationnellede langages imperatifs. Le probleme principal est de definir la semantique desboucles, et l’on utilise pour ce faire l’artillerie des CPOs, et le theoreme deKleene. On note en passant le lien entre fonctions continues au sens des CPOs(sur les entiers naturels) et les fonctions calculables du chapitre 5.

Le chapitre 7 introduit les concepts de la logique classique du premier ordrenecessaires a la comprehension de la preuve de programmes (chapitre 8) et autypage (chapitre 9). On y introduit, rapidement, au probleme de la satisfac-tion de formules en logique des predicats du premier ordre, et a la theorie de lademonstration, d’un fragment de cette logique (la logique propositionnelle quan-tifiee du premier ordre). La presentation du probleme de satisfaction, dans unmodele quelconque, est traitee de facon legerement non-standard, dans le sensou on le transcrit en une semantique denotationnelle, comme pour les langagesde programmation, au chapitre 6. Ceci permet d’attirer l’attention de l’etudiantau parallele qu’il y a entre logique, et programmes.

On derive des chapitres 6 et 7 une methode de validation de programmes(imperatifs), par la preuve a la Hoare. Comme au chapitre 7, on montre qu’helas,tous ces problemes sont generalement indecidables, c’est-a-dire qu’il n’existe pasde methode automatique qui peut valider tout programme, en temps fini. Cecipourrait etre une bonne introduction, au theoreme de Rice d’une part, et auxmethodes d’approximations en validation, comme l’interpretation abstraite, quel’on ne traite pas ici.

Le chapitre 9 revient sur la programmation fonctionnelle, et introduit unlangage jouet, PCF, afin de mettre en lumiere certains phenomenes que memedes programmeurs Caml n’ont sans doute pas remarques. Le premier est celui del’ordre d’evaluation dans les appels de fonctions. On en donne une semantiqueoperationnelle, qui est une autre grande famille de semantiques de langages deprogrammation possible. En examinant de pres ces semantiques, on comprendsous une autre forme, le paradigme de passage d’argument par valeur et parreference, vu au chapitre 2, dans le cadre de langages imperatifs, et on en de-couvre un autre, le passage par necessite, a la base du langage fonctionnel Has-kell. L’autre point est le typage, qui paraıt si naturel au programmeur Caml :on montre en fait qu’il s’agit d’une forme de preuve (au sens de la theorie dela demonstration, chapitre 7) de bon comportement des programmes. Ceci estune bonne introduction a l’isomorphisme de Curry-Howard et a sa descendancenombreuse.

On termine ce cours avec un paradigme sans aucun doute original pour leprogrammeur classique, les langages reactifs synchrones, tels Lustre. Ce sontdes langages non seulement tres utiles en pratique, pour la programmation decontrole-commande par exemple, mais qui ont aussi une semantique tres propre,dont les origines remontent aux reseaux de Kahn. Cela nous permet, une dernierefois, d’utiliser le cadre theorique elegant des CPOs du chapitre 6.

Page 9: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

9

Remarques et remerciements Ces notes forment un polycopie de cours trespreliminaire de INF 321. Ils sont en quelque sorte les transparents commentes,en attendant une version plus complete. Je remercie Sylvie Putot d’avoir bienvoulu relire la premiere version de ce polycopie.

Page 10: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

10 CHAPITRE 1. INTRODUCTION

Page 11: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Chapitre 2

Les fondements de laprogrammation imperative

Les langages imperatifs, comme C et Java, ont tous en commun l’utilisationde cinq constructions, qui constituent ce que l’on appelera le noyau impera-tif : la declaration de variables, l’affectation d’une expression a une variable, lasequence, le test et la boucle.

2.1 Variables et types

Avant de demarrer, il nous faut parler du concept de variable, dans les lan-gages informatiques. C’est une notion un peu differente de la notion mathe-matique. En mathematiques, les variables sont quantifiees, le nom importe peu(∀x, P (x), ∃x, P (x)), seul le lien avec P importe. En informatique, une variableest une abstraction d’une location memoire (ou adresse memoire)

x : 8 y : 1 z : 6 t : 3

Les variables permettent en premier lieu de stocker des calculs intermediaires,et de les reutiliser. C’est meme plus profond que cela. Pour effectuer certainestaches en un temps raisonnable, il est necessaire d’occuper de la memoire, il ya une relation entre les classes de complexite en temps et en espace, que l’onverra brievement au chapitre 5.

Syntaxiquement, une variable est un mot d’une ou plusieurs lettres (soumisa certaines regles, cf. le memento Java), par exemple x, y, resultat1 etc.

Aux variables sont generalement associes des types, on y reviendra pour laprogrammation fonctionnelle au chapitre 9.

Un type decrit et structure un ensemble de valeurs possibles (comme, enmathematique, R, N, R2 etc.). Il existe des types elementaires (valeurs simples),types composes (tableaux, enregistrements etc.). Encore une fois, cela est bienplus profond, on verra cela au chapitre 3.

11

Page 12: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

12 CHAPITRE 2. PROGRAMMATION IMPERATIVE

Les types elementaires sont– int : nombres entiers compris entre −231 et 231−1 ; types similaires, byte,short, long, (et en C : long long...)

– boolean (false, true) - pas en C– float, type similaire double– char : ex. ’g’Avant d’utiliser une variable, il faut d’abord la declarer et declarer son type,

en Java. Puis il faut reserver un emplacement memoire (“allocation”). Pour lestypes simples, cette allocation est faite a la declaration. On peut generalementdeclarer, allouer et initialiser en meme temps une variable (attention en C nean-moins, il existe quelques regles syntaxiques).

Voici quelques exemples simples :En Java :

int x=3;

En C :

int x=3;

En Caml :

let x=r e f 3 in p ; ;

Ce code est un peu plus complique en fait, car il y a une notion de portee : xest connu uniquement dans p.

Les langages de programmation (“haut-niveau”) sont structures, il existe unenotion de bloc ; par exemple dans une fonction, ou le corps d’une boucle etc.Une variable peut etre connue dans un bloc, mais pas a l’exterieur. Cela permetd’appliquer une methodologie saine de codage ; structurer le code, et cloisonnerles informations, on verra cela plus en detail au chapitre 3.

Il existe aussi une notion de variable finale en Java : ce sont des variables nepouvant etre affectees qu’une fois (ce sont les equivalents const C). L’idee estque les variables finales ne peuvent etre modifiees apres une premiere affectation,elles sont en fait constantes. Donc le code suivant est correct :

f ina l int x=4;y=x+3;

Par contre, celui-ci est incorrect :

f ina l int x=4;x=3;

Le contraire des variables finales s’appelle les variables mutables. Cela nouspermet de donner une explication rapide du ! en Caml. La version finale de lavariable x dans le code suivant :

let x=4 in y=x+3

Alors que la version mutable est :

let x=r e f 4 in y=!x+3

Page 13: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2.2. CODAGE DES NOMBRES 13

En fait, le ref veut dire que x contient en fait l’adresse de la location me-moire contenant la valeur 4, et !x permet, a partir de l’adresse contenue dansx, de rapatrier la valeur de la location memoire correspondante. On reviendra al’explication precise de cela a la section 2.7.

2.2 Codage des nombres

Un int est code sur 32 bits, en base 2 (signe code par complementation).Donc x=19 est code par le mot sur 32 bits :

00000000000000000000000000010011On peut jongler avec la representation binaire, decalage a gauche : 19 << 1

(...100110=38), a droite 19 >> 1 (...1001=9), masquage (&, |) etc.Pour les types float et double, la difference avec les nombres ideaux (les

reels dans ce cas), est encore pire, d’une certaine facon : Il s’agit d’un codageen precision finie : la mantisse est codee en base 2, l’exposant egalement, et letout sur un nombre fini de bits (cf. norme IEEE 754).

Attention, a cause de tout cela, et des erreurs d’arrondi dues au nombrefini de bits utilises pour le codage des nombres, il n’y a pas associativite del’addition, multiplication etc.

Considerons le programme suivant :

f loat x , y ;x = 1 .0 f ;y = x+0.00000001 f ;

Alors, x et y ont la meme valeur !Un grand classique (Kahan-Muller) de programme qui donne un resultat

surprenant est le suivant :

f loat x0 , x1 , x2 ;x0=11/2;x1=61/11;for ( i =1; i<=N; i++) x2=111 − (1130−3000/x0 )/ x1 ;x0=x1 ;x1=x2 ;

Une execution produit la suite : 5.5902 ; 5.6333 ; 5.6721 ; 5.6675 ; 4.9412 ; -10.5622 ; 160.5037 ; 102.1900 ; 100.1251 ; 100.0073 ; 100.0004 ; 100.0000 ; 100.0000 ;100.0000 ; ...(stable) alors que la vraie limite dans les reels est 6 !

Ou un autre exemple, beaucoup plus simple.

f loat x0 , x1 ;x0=7/10;

Page 14: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

14 CHAPITRE 2. PROGRAMMATION IMPERATIVE

for ( i =1; i<=N; i++) x1 = 11∗x0 − 7 ;x0 = x1 :

Ce programme produit la suite 0.7000 ; 0.7000 ; 0.7000 ; 0.6997 ; 0.6972 ;0.6693 ; 0.3621 ; -3.0169 ; -40.1857 ; -449.0428 ; -4946.4712 ; -54418.1836 ; -598607.0000 ;-6584684.0000 ; diverge vers −∞, alors que dans les reels c’est la suite constanteegale a 0.7 !

2.3 Expressions arithmetiques

On appelle expression, tout element de syntaxe forme a partir de constanteset de variables avec des operations (+, -, *, / etc.).

Voici un exemple d’expression en Java ou C :

3+y ∗5 ;

En C et Caml, cela est tres similaire : en C par exemple :

3+y ∗5 ;

En Caml :

3+(!y )∗5

A part la syntaxe, cela est tres similaire, on verra la notion de ! plus tard.

2.4 Instructions

Une instruction est un mot simple dans un langage de programmation, quifait une action. C’est un concept qui est donc different d’une expression. On apar exemple comme instructions : l’affectation, le test, la boucle.

2.5 Semantique elementaire

On va commencer a introduire une maniere formelle de decrire l’execution detelles instructions dans les langages de programmation. Il s’agit de la semantiquedes langages de programmation.

A chaque variable v ∈ Var, on veut associer une valeur. On definit unefonction ρ ∈ Env :

ρ : Var→ Val

ou Val est l’ensemble des valeurs machines (Z ici pour simplifier). et Env estl’ensemble des environnements. A chaque expression arithmetique e ∈ AExpr,on veut associer une valeur :

[[e]]ρ ∈ Val

Page 15: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2.5. SEMANTIQUE ELEMENTAIRE 15

de l’expression e dans l’environnement ρ. A chaque instruction du programmep (et donc au programme lui-meme), on veut associer un environnement [[p]]ρ,apres execution, dans l’environnement ρ.

On donne ainsi des regles inductives pour donner la semantique, tout d’abord,des expressions :

– Constantes n ∈ Val : [[n]]ρ = n– Variables v ∈ Var : [[v]]ρ = ρ(v)– Addition de deux sous-expressions a0, a1 ∈ AExpr : [[a0 + a1]]ρ = [[a0]]ρ+

[[a1]]ρ– [[a0 − a1]]ρ = [[a0]]ρ− [[a1]]ρ– [[a0 ∗ a1]]ρ = [[a0]]ρ[[a1]]ρ– etc.On l’appelle semantique denotationnelle (ou compositionnelle).Donnons-en un exemple : dans l’environnement ρ ou ρ(y) = 3 :

[[3 ∗ y + 5]]ρ = [[3 ∗ y]]ρ+ [[5]]ρ= [[3 ∗ y]]ρ+ 5= ([[3]]ρ) ([[y]]ρ) + 5= 3 ∗ 3 + 5= 14

Attention, tout ceci est valide tant que l’on considere des expressions sanseffet de bord. Les effets de bord sont permis en JAVA :

int x = (y=1)+1;

Cela renvoie l’environnement : ρ avec ρ(y) = 1, ρ(x) = 2, mais n’est pas permisdans notre semantique elementaire.

Voyons maintenant la semantique de notre premiere instruction : l’affecta-tion.

x = expr ;

Par exemple :

x = 3+y ∗5 ;

Cette instruction lit les valeurs de variables dont depend l’expression (enpartie, et en general dans les registres du processeur) puis calcule l’expression(avec ou sans effet de bord), et remplace la valeur contenue dans x par cellecalculee.

Remarque importante : L’affectation est aussi une expression, donc x=expra une valeur, qui est celle de l’expression.

Pour l’affectation, la semantique peut s’ecrire de la facon suivante : pourv ∈ Var ; e ∈ AExpr, et ρ ∈ Env :

[[v = e]]ρ = ρ[[[e]]ρ/v]

ou, pour u, v ∈ Var et n ∈ Val :

ρ[n/v](u) =ρ(u) si u 6= vn si u = v

Page 16: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

16 CHAPITRE 2. PROGRAMMATION IMPERATIVE

Ce nouvel environnement est le meme que ρ, mis a part pour la variable u, dontl’ancienne valeur est “ecrasee” par l’affectation a n.

On verra la semantique d’autres instructions plus tard, en particulier au cha-pitre 6. Nous introduisons ces premieres notations ici afin de pouvoir introduireprecisement dans ce chapitre et le suivant, les notions d’adresse et de valeur.

Donnons pour l’instant un exemple simple d’interpretation d’une affectationsimple. Considerons x = 3 ∗ y + 5 dans l’environnement ρ tel que ρ(y) = 3 etρ(x) = 1, on obtient l’environnement σ avec

σ(u) =ρ(u) = 3 si u = y[[3 ∗ y + 5]]ρ = 14 si u = x

2.6 Les tableaux

Introduisons maintenant dans la semantique de l’affectation un type de don-nees un peu plus complique, les tableaux. Les tableaux sont un cas particulierde type produit, que l’on traite plus generalement au chapitre 3. Un tableaupeut etre vu comme un ensemble de variables pour chaque indice 1D, 2D, oumulti-dimensionnel. Dans ce chapitre, on va juste considerer les tableaux 1D(c’est-a-dire les vecteurs).

A la declaration d’un tableau, la taille n’est pas forcement connue, aucunelement n’est encore alloue, contrairement aux types de donnees “scalaires” telsles entiers, ou les flottants :

int [ ] tab ;

On peut donc montrer ce qui se passe en memoire, par un petit graphe :

tab

tab est cree, a une location memoire associee, mais qui n’est pas initialisee, quine contient donc pas de reference (c’est-a-dire d’adresse memoire) a un bloc dememoire pouvant contenir tous les elements du tableau.

Il faut maintenant effectuer l’allocation du tableau : pour ce faire, on fixe lataille une fois pour toute, et les elements sont alloues :

tab = new int [ 1 0 ] ;

On a donc maintenant en memoire la situation suivante :

tab

? | ? | . . . | ?

Page 17: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2.7. SEMANTIQUE DES REFERENCES 17

(en fait, il y a des valeurs par defaut en Java, ici ? est egal a 0)Maintenant, on peut proceder a l’affectation des elements de tableau. Les

indices vont ici de 0 a 9 (=tab.length-1) :

tab [ 0 ] = 3 ;

On a donc maintenant en memoire :

tab

3 | ? | . . . | ?

On peut aussi faire les deux en meme temps :

int [ ] tab = 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 ;

Par contre, on n’a pas le droit d’ecrire :

int [ ] tab ;tab = 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 ;

2.7 Semantique des references

Il va falloir raffiner notre semantique elementaire de l’affectation de la section2.5 pour tenir compte des references, ce aussi bien pour donner une semantiquea l’affectation des tableaux que pour l’affectation des variables mutables.

Le tableau tab est en fait un pointeur (ou reference, ou adresse memoire). Ilnous faut donc un environnement un peu plus complique, pour pouvoir parlerdes variables scalaires, et des tableaux.

Soit Loc l’ensemble des adresses memoires (numerotation des mots 32 ou64 bits dans la memoire de quelques Go de votre ordinateur), sous-ensemble deN. Les environnements sont maintenant ρ : (Var ∪ Loc) → (Val ∪Var ∪ Loc)en toute generalite. Var represente les adresses allouees statiquement, en pile(les declarations de variables scalaires...), Loc represente les adresses alloueesdynamiquement, en general sur le tas. On notera adrt pour l’adresse t, pourqu’il n’y ait pas de confusion avec Val.

En premiere approximation, pour le code suivant :

let x=4 in y=x+3

On arrive a l’environnement final : ρ(x) = 4, ρ(y) = 7. Alors que pour lecode (avec la version mutable de x :

let x=r e f 4 in y=!x+3

On arrive a l’environnement final : ρ(x) = adru, ρ(adru) = 4, ρ(y) = 7.Dans le cas mutable, x contient une valeur de type “adresse” (dans Loc) ;

dans le cas final, x contient une valeur “scalaire”.

Page 18: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

18 CHAPITRE 2. PROGRAMMATION IMPERATIVE

Revenons maintenant a la semantique des tableaux. On considere d’abord ladeclaration en Java :

int [ ] tab ;

L’environnement est apres cette declaration : ρ(tab) = ⊥ : on utilise iciun nouveau symbole, qui n’est egal a aucune valeur, et que l’on lit “bottom”,dont nous expliciterons mieux l’origine au chapitre 6. Il nous permet de coderle fait qu’aucune valeur n’est associee encore a tab. Venons-en a l’allocationmaintenant :

tab = new int [ 1 0 ] ;

On obtient l’environnement : ρ(tab) = adrt ou adrt est l’adresse de lapremiere case memoire allouee pour tab. Sont cre’ees egalement les adressesadrt+1, adrt+2,. . .,adrt+9, mais pas les valeurs correspondantes : ρ(adrt) = ⊥,ρ(adrt+1) = ⊥,. . .,ρ(adrt+9) = ⊥.

De facon generale, voici les regles definissant la semantique des tableaux 1D.Pour tab ∈ Var, i ∈ N, e ∈ AExpr, et ρ ∈ Env :

[[tab[i] = e]]ρ = ρ[[[e]]ρ/(ρ(tab) + i)]

Et pour le calcul dans une expression e ∈ AExpr, il faut rajouter dans lesexpressions arithmetiques le fait de lire une entree d’un tableau :

[[tab[i]]]ρ = ρ(ρ(tab) + i)

Cette regle s’interprete de la facon suivante : on lit l’adresse de tab, on la decalede i, puis on lit la valeur a cette adresse - ici on suppose qu’on n’a que destableaux 1D de valeurs scalaires.

Donnons-en un exemple. Considerons le code Java suivant :

int [ ] tab = new int [ 1 0 ] ;tab [ 0 ]=0 ; tab [ 1 ]=1 ; tab [ 2 ]=2 ; . . . tab [ 9 ]=9 ;

Ont ete creees donc, dans l’environnement, les adresses adrt a adrt+9 etρ(tab) = adrt. On termine avec en plus ρ(adrt+i) = i pour i = 0, . . . , 9.

2.8 La sequence d’instructions

La sequence d’instructions consiste juste a mettre en serie une instructionpuis une deuxieme (et eventuellement ainsi de suite). En Java ou C, par exemple,voici deux instructions d’affectation en sequence :

x=1; y=x+1;

On execute x=1 puis y=x+1.Et enfin en Caml :

let x=1 in let y=x+1 in p ; ;

Page 19: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2.9. CONDITIONNELLES 19

Pour P et Q deux programmes composes sequentiellement, la semantiqueest facile a ecrire :

[[P ;Q]]ρ = [[Q]] ([[P ]]ρ)

C’est precisement cette regle qui fait qu’on appelle cette semantique, com-positionnelle.

2.9 Conditionnelles

Les conditionnelles permettent de choisir entre deux executions possibles,selon le resultat d’un test. En Java, et C cela s’ecrit :

i f (b)p1

elsep2

On calcule la valeur booleenne de b ; si c’est true on execute p1, si c’estfalse on execute p2. Par exemple :

i f ( x==0)y=−1;

elsey=1;

Attention a l’erreur courante : if (x=true) p1 else p2 fera toujours p1,alors qu’on voulait ecrire a priori if (x==true) ... Attention egalement a bienmettre dans un bloc toutes les instructions a executer dans la branche vraie oudans la branche fausse, quand il y en a plusieurs, c’est-a-dire de les mettre entreaccolades.

En Caml cela est tres similaire syntaxiquement :

i f b then p1 else p2

Les expressions pour les tests booleens sont assez simples syntaxiquement,en Java, C et Caml. Cela peut etre n’importe quelles conditions separees pardes connecteurs logiques :

&&, | | , ( expr1==expr2 ) , . . .

Les expressions booleennes permises forment l’ensemble BExpr ; les valeursVal sont supposees comprendre egalement les booleens true, false. Alors onpeut ecrire une semantique des expressions booleennes, comme on l’avait faitpour les expressions arithmetiques, inductivement sur la syntaxe :

– [[true]]ρ = true– [[false]]ρ = false

– Si a0, a1 ∈ AExpr, [[a0 == a1]]ρ =true si [[a0]]ρ = [[a1]]ρfalse sinon

– Si a0, a1 ∈ AExpr, [[a0 < a1]]ρ =true si [[a0]]ρ < [[a1]]ρfalse sinon

– De meme pour a0 <= a1 etc.

Page 20: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

20 CHAPITRE 2. PROGRAMMATION IMPERATIVE

Pour la semantique de la conditionnelle :

[[if b p1 else p2]]ρ =

[[p1]]ρ si [[b]]ρ = true[[p2]]ρ si [[b]]ρ = false

2.10 Iteration - la boucle

Dans tous les langages iteratifs, on a une facon d’iterer des calculs par uneconstruction de type boucle. Par exemple, en Java et C :

while (b)p

En voici un exemple pratique, qui itere l’incrementation de x de 2, jusqu’ace que x soit superieur ou egal a 1000 :

while (x<1000)x=x+2;

En Caml, on a une construction similaire :

while b do p ;

b est appele la condition de terminaison. La boucle est un raccourci pourtous ses deroulements finis, on verra au chapitre 6 sa semantique formelle, parapproximations successives :

i f (b) pi f (b)

pi f (b)

pi f (b)

p. . .

. . . et ainsi de suite. . .Les approximations successives forment une suite finie quand la condition de

terminaison devient vraie en un temps fini. Mais toutes les boucles ne sont pasfinies, par exemple :

int x=3;while (x<=1000)

x=2;

En general, il est difficile de prouver la terminaison en general (ce sera prouvedans un cadre precis au chapitre 8). Par exemple : la terminaison du code suivantreposerait sur une preuve de la conjecture de Syracuse :

Page 21: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2.11. FONCTIONS 21

while ( x > 1) i f ( x % 2 == 0)

x = x /2 ;else

x = 3∗x+1;

Le fait que toutes les boucles ne terminent pas fait que [[P ]]ρ ne peut donctoujours renvoyer un σ ∈ Env. En fait, σ ne peut etre qu’une fonction partielle,ou a recodage pres, une fonction totale a valeur dans Env ∪ ⊥, que l’on noteEnv⊥. Par abus de notation, on notera ⊥ ∈ Env⊥, l’environnement tel que ∀x,⊥(x) = ⊥. On aura alors pour un programme P : [[P ]]ρ = ⊥ si P ne termine pasdans l’environnement ρ ;

2.11 Fonctions

Imaginons le programme suivant, ecrit en Java ou en C :

/∗ Calcu l de x ∗/. . .

i f (x<0) sgnx=−1;i f ( x==0) sgnx=0; else sgnx=1;

. . ./∗ Calcu l de y ∗/

i f (y<0) sgny=−1;i f ( y==0) sgny=0; else sgny=1;

Ce code n’est pas pratique, il y a redite pour le calcul du signe, et celaest une cause potentielle d’erreur. Il est un bon principe que de structurer lecode par des appels a des fonctions, de plus en plus elementaires. En fait, l’ideede fonction dans les langages de programmation est plus profond que cela, enparticulier dans le cadre de la recursion et des langages fonctionnels (Caml), oule credo est : “Functions as first-class citizens” (Christopher Strachey, ∼1960).On verra cela plus en detail aux chapitres 5 et 10.

Une fonction peut : prendre en compte des donnees, les arguments, retournerune valeur, le retour de la fonction, et effectuer une action (afficher quelquechose, modifier la memoire), l’effet de bord. Dans la plupart des langages, onindique le type des arguments et du retour de fonction (sauf en general dansCaml, ou il y a inference de types - cf. chapitre 10), par exemple int, float.

On appelle generalement procedure une fonction sans retour de valeur. Gene-ralement on identifie cela avec le fait de retourner une valeur dans un“type”vide.En Java, et C, ce type est nomme par le mot cle void. en Caml, c’est le typespecial unit.

2.12 Passage d’arguments aux fonctions

Decrivons maintenant le mecanisme de passage d’arguments dans les fonc-tions Java (ou C). Considerons le code Java, ou C :

Page 22: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

22 CHAPITRE 2. PROGRAMMATION IMPERATIVE

stat ic int s i gne ( int x ) i f (x<0) . . .

Ou encore en Caml :

let s i gne x = i f (x<0) then . . .

Il existe egalement des fonctions predefinies, par exemple dans les paquetages(ou“modules”, “librairies”) de fonctions mathematiques Math, en particulier, parexemple la fonction racine carree : Math.sqrt(...).

Reprenons le code precedent, ou l’on remplace le code de calcul du signe,duplique, par un appel a une fonction signe :

/∗ Calcu l de x ∗/. . .sgnx = s i gne (x ) ;/∗ Calcu l de y ∗/. . .sgny = s i gne (y ) ;. . .

Les arguments de la fonction signe sont evalues avant de demarrer l’execu-tion de son code meme (on aurait pu faire signe(expr donnant x)). C’est ceque l’on appelle le “passage par valeur”.

En Caml, il s’agit de meme d’un passage par valeur, dans le code similairesuivant :

. . .inlet sgnx = s i gne x in . . . ; ;

Passons maintenant au retour de fonction.En Java, la fonction signe est ecrite comme suit :

stat ic int s i gne ( [ f ina l ] int x ) i f (x<0) return −1;i f ( x==0) return 0 ;return 1 ;

De meme en C :

int s i gne ( [ const ] int x ) i f (x<0) return −1;i f ( x==0) return 0 ; return 1 ;

Et en Caml :

let s i gne x = i f (x<0) then −1.0 elsei f ( x==0) then 0 .0 else 1 . 0 ; ;

La semantique de l’appel/retour de fonction est la suivante : return inter-rompt le deroulement de la fonction :

Page 23: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2.12. PASSAGE D’ARGUMENTS AUX FONCTIONS 23

call signe(-3) signe(x)

return -1 x<0

return 0 x==0

return 1

yes

no

yes

no

Prenons maintenant un nouvel exemple de code, qui intervertit les valeursdes variables a et b, en utilisant la variable intermediaire c :

class t r o i sV e r r e s public stat ic void main ( St r ing [ ] a rgs )

int a=1;int b=2;int c=a ;a=b ;b=c ;System . out . p r i n t l n ( ”a=”+a ) ;System . out . p r i n t l n ( ”b=”+b ) ;

java troisVerres donne bien a=2, b=1.Ecrivons maintenant un nouveau code par appel de fonction :

class t r o i sV e r r e s stat ic void swap ( int x , int y )

int z=x ; x=y ; y=z ;

public stat ic void main ( St r ing [ ] a rgs ) int a=1;int b=2;swap (a , b ) ;System . out . p r i n t l n ( ”a=”+a ) ;System . out . p r i n t l n ( ”b=”+b ) ;

java troisVerres donne a=1, b=2, qui n’est pas le resultat espere. Ceci estbien sur du au mecanisme de passage par valeur. En Java, C, Caml, on n’a pardefaut que le passage par valeur (des variables scalaires). La fonction travailledans ce cas sur une copie des variables passees en parametre.

Page 24: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

24 CHAPITRE 2. PROGRAMMATION IMPERATIVE

call swap aswap ← amain bswap ←bmain

swap aswap, bswap

return ;

copie args

swap local

La modification des valeurs des variables n’a aucune portee au dela de la fonc-tion.

2.13 Variables locales, variables globales

Ceci mene naturellement au concept de portee des variables. On a parexemple des variables connues du programme entier, ce que l’on appelle desvariables globales :

class C stat ic int x ; x=3; x=0;

x vaut 0 a la fin de l’execution de ce programme. Inversement, on peut definirdes variables locales a un bloc ou a une fonction :

stat ic void r e s e t ( ) int x=0; public stat ic void main ( St r ing args [ ] )

int x ; x=3; r e s e t ( ) ; . . .

x dans ce cas recouvre deux variables :– une variable locale a main, qui vaut 3 et n’est pas modifiee– une variable locale a la fonction reset, qui vaut 0, et qui n’est connue que

dans le corps de la fonction resetCette variable locale cache la variable x de main dans le corps de la fonction

reset, mais ne l’ecrase pas (location distincte).Regardons maintenant le cas du passage de tableaux en parametre, avec ce

petit code exemple :

stat ic void tab p lus un ( int [ ] y )int n = y . l ength ;for ( int i = 0 ; i < n ; i=i +1)

y [ i ] = y [ i ]+1; public stat ic void main ( St r ing [ ] a rgs )

int [ ] x = new int [ 5 ] ;for ( int i = 0 ; i < 5 ; i=i +1)

x [ i ] = i ;tab p lus un (x ) ;for ( int i = 0 ; i < 5 ; i=i +1)

System . out . p r i n t l n ( ”x [ ”+i+”]=”+x [ i ] ) ;

Page 25: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2.13. VARIABLES LOCALES, VARIABLES GLOBALES 25

Son execution termine avec :

x [0 ]=1 x [1 ]=2 x [2 ]=3 x [3 ]=4 x [4 ]=5

ce qui semble contradictoire avec la semantique du passage d’arguments parvaleur. En fait, le passage de tableaux en parametre se fait par reference. Untableau n’est pas un type simple (scalaire) mais compose. Il est trop lourd defaire des recopies a l’appel de fonction, on ne passe donc que sa reference (poin-teur, adresse memoire...) au tableau, a l’appel de la fonction. Cela permet defaire des modifications en place comme on l’a fait dans ce programme.

Voici ce qui se passe a chaque etape d’execution :

for ( i =0; i <5; i=i +1)x [ i ] = i ;

x

0 | 1 | . . . | 4

Puis :

tab p lus un (x ) . . .

x y

0 | 1 | . . . | 4

Dit autrement, avec notre semantique formelle : ρ ∈ Env⊥ au moment del’appel est : ρ(x) = adru, ρ(y) = adru, ρ(adru+i) = i pour i = 0, . . . , 4.

Puis on execute :

. . .y [ i ] = y [ i ]+1;

x y

1 | 2 | . . . | 5

Par la semantique que l’on vient de voir, on en deduit en effet que : ρ′ =[[y[i] = y[i] + 1]]ρ = ρ[[[y[i] + 1]]ρ/(ρ(y) + i)], et [[y[i] + 1]]ρ = ρ(ρ(y) + i) +1 = ρ(adrt+i) + 1 = i + 1. D’ou a la fin, le seul changement entre ρ et ρ′ estρ′(adrt+i) = i+ 1.

Et on termine l’execution avec :

. . .System . out . p r i n t l n . . .

x

1 | 2 | . . . | 5

Page 26: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

26 CHAPITRE 2. PROGRAMMATION IMPERATIVE

Par la semantique que l’on vient de voir : [[x[i]]]ρ′ = ρ′(ρ′(x) + i). Donc,[[x[i]]]ρ′ = ρ′(adrt+i) = i+ 1.

2.14 References, pointeurs, objets

Reprenons maintenant le code de la classe troisVerres, ou on utilise unefonction pour faire l’echange de valeurs entre deux variables :

class t r o i sV e r r e s stat ic void swap ( int x , int y )

int z=x ; x=y ; y=z ;

public stat ic void main ( St r ing [ ] a rgs ) int a=1;int b=2;swap (a , b ) ;System . out . p r i n t l n ( ”a=”+a ) ;System . out . p r i n t l n ( ”b=”+b ) ;

On termine toujours avec le resultat errone : a=1, b=2.De meme pour le code Caml :

let swap x y =let z = r e f 0 in ( z :=! x ; x :=! y ; y :=! z )

a :=1;b :=2;swap a b ;p r i n t i n t ! a ;p r i n t new l i n e ( ) ;p r i n t i n t ! b ;p r i n t new l i n e ( ) ;

Ce que l’on voudrait, en fait, pour faire marcher ce code, c’est passer la re-ference aux variables a et b plutot que simplement leurs valeurs, afin de pouvoirmodifier les valeurs en place. En Pascal c’etait possible :

procedure toto (var integer i , integer j ) ;begin

. . .end

Dans le code plus haut, i est passee par reference, j est passee par valeur.En Java, C, Caml, il va nous falloir simuler ce passage, par le passage par valeur.

Commencons par du C, ou la manipulation d’adresse (pointeur) est explicite.Pour trouver la location d’une variable a partir de son nom, en C, on utilise lemot cle &. Voici les differentes etapes d’execution d’un programme simple demanipulation d’un pointeur sur un entier ptrx :

Page 27: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2.14. REFERENCES, POINTEURS, OBJETS 27

int x , y ;int ∗ptrx ;

x = 1 ;

ptrx

x : 1

Puis on recupere l’adresse memoire de x que l’on stocke dans ptrx :

y = 0 ;ptrx = &x ;

ptrx

x : 1 y : 0

ptrx contient ainsi l’adresse en memoire ou est stockee la valeur de la variablex. Ensuite, on ecrase la valeur courante de y, par la valeur pointee par la variablesptrx :

int x , y ;int ∗ptrx ;

x = 1 ;y = 0 ;ptrx = &x ;

y=∗ptrx ;p r i n t f ( ”y=%d\n” , y ) ;

Ceci donne bien sur y=1.Si on avait plutot fait :

int x , y ;int ∗ptrx ;

x = 1 ;y = 0 ;ptrx = &x ;

x = 2 ;

ptrx

x : 2 y : 0

Puis :

Page 28: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

28 CHAPITRE 2. PROGRAMMATION IMPERATIVE

int x , y ;int ∗ptrx ;

x = 1 ;y = 0 ;ptrx = &x ;

x = 2 ;y = ∗ptrx ;p r i n t f ( ”y=%d\n” , y ) ;

ptrx

x : 2 y : 2

On aurait obtenu y=2.Enfin, pour en terminer avec la syntaxe, notons qu’en Caml, * est l’homo-

logue du ! de Caml, et *t=u l’homologue de t :=u de Caml.En supposant que x est en quelque sorte“bien type”dans l’environnement ρ :

ρ(x) ∈ Loc, on peut donner une semantique a l’operation ∗. Dans une expression(AExpr), on aura :

[[∗x]]ρ = ρ([[x]]ρ)

Cette ecriture, au lieu d’ecrire directement ρ(x) permet de generaliser x a desexpressions de certains types, comme &y, ce que l’on verra dans un exemple,plus bas.

Dans une affectation, la semantique est la suivante :

[[∗x = e]]ρ = ρ[[[e]]ρ/ρ(x)]

Notez le niveau d’indirection, encore, dans cette regle semantique.Pour x ∈ Var, et ρ : (Var ∪ Loc) → (Val ∪Var ∪ Loc). On pose dans une

expression (AExpr) :

[[&x]]ρ = x ∈ Var

(remarque : on ne peut utiliser &x a gauche d’une affectation)Donnons-en un exemple simple :

[[∗(&x)]]ρ = ρ([[&x]]ρ)= ρ(x)

En Java, un objet est en fait un pointeur (vers la location memoire contenanttoutes les informations sur l’objet). Un scalaire est une valeur, pas un pointeurvers une valeur.

On peut ainsi reprendre, cette fois-ci correctement, le code d’echange devaleurs, en passant en argument a swap, par valeur, les references a des objetscontenant une unique valeur enti ‘ere :

Page 29: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2.14. REFERENCES, POINTEURS, OBJETS 29

class t r o i sV e r r e s public stat ic void main

( St r ing [ ] a rgs ) I n tva l a=new I n tva l ( ) ;a . va l =1;In tva l b=new I n tva l ( ) ;b . va l =2;swap (a , b ) ;System . out . p r i n t l n ( ”a=”+a . va l ) ;System . out . p r i n t l n ( ”b=”+b . va l ) ;

class I n tva l public int va l ;. . .

Dans ce programme, int val ; dans class Intval n’est pas static. Celaveut dire que plusieurs “elements” de “type” Intval peuvent etre definis, avecdes valeurs entieres val differentes (on reviendra la-dessus au chapitre 4). L’ins-truction new (comme sur les tableaux) permet d’allouer un nouvel element detype Intval.

Quand on fait a = new Intval() ;, cela cree la case memoire associee a a(pouvant contenir val, entier). Cela fait un appel a une fonction cachee : leconstructeur par defaut Intval().

Donc la declaration d’un objet Java a pour effet de creer un simple empla-cement memoire pour stocker une adresse :

I n tva l a ; a : ?

Puis vient l’allocation memoire, qui permet de trouver une adresse valide enmemoire pour stocker l’objet lui-meme :

I n tva l a ;a = new I n tva l ( ) ;

a

val : ?

Et enfin l’affectation d’un valeur au contenu de cet objet :

I n tva l a ;a = new I n tva l ( ) ;a . va l = 1 ;

a

val : 1

On peut aussi utiliser un raccourci pour l’allocation et affectation, en defi-nissant un nouveau constructeur :

a = new I n tva l ( ) ;a . va l =1;b = new I n tva l ( ) ;b . va l =2;

−→public I n tva l ( int u) va l=u ;

a = new I n tva l ( 1 ) ;b = new I n tva l ( 2 ) ;

Page 30: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

30 CHAPITRE 2. PROGRAMMATION IMPERATIVE

Cette nouvelle fonction (constructeur) doit s’appeler du meme nom que leprogramme et ne pas avoir de type ; on l’appelle par new .... Il existe toujoursun constructeur par defaut sans argument (pas besoin de le definir)

Pour en terminer, voici comment coder l’echange de facon correcte :

stat ic void good swap ( In tva l u ,I n tva l v )

int w = u . va l ;u . va l = v . va l ; v . va l = w;

Une mauvaise version est :

stat ic void wrong swap ( In tva l u ,I n tva l v )

I n tva l w = u ;u = v ; v = w;

Les differentes etapes de l’execution de wrong_swap sont :

a = new I n tva l ( ) ;a . va l =1;b = new I n tva l ( ) ;b . va l =2;

a b

val : 1 val : 2

Puis,

a = new I n tva l ( ) ;a . va l =1;b = new I n tva l ( ) ;b . va l =2;wrong swap (a , b ) ;

a u b v

val : 1 val : 2

On passe par la variable intermediaire :

I n tva l w = u ;

a u w b v

val : 1 val : 2

Puis :

u = v ;

a w b v u

val : 1 val : 2

Page 31: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2.14. REFERENCES, POINTEURS, OBJETS 31

Et on termine par :

v = w;

a v w b u

val : 1 val : 2

Il est clair que le resultat n’est pas le bon...Passons maintenant en revue l’execution de good_swap :

a = new I n tva l ( ) ;a . va l =1;b = new I n tva l ( ) ;b . va l =2;

a b

val : 1 val : 2

On continue par l’appel a la fonction swap :

a = new I n tva l ( ) ;a . va l =1;b = new I n tva l ( ) ;b . va l =2;good swap (a , b ) ;

a u b v

val : 1 val : 2

Puis l’utilisation de la variable intermediaire :

int w = u . va l ;

a u b v

val : 1 val : 2 w : 1

Et :

u . va l = v . va l ;

a u b v

val : 2 val : 2 w : 1

Et enfin :

Page 32: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

32 CHAPITRE 2. PROGRAMMATION IMPERATIVE

v . va l = w;

a u b v

val : 2 val : 1 w : 1

Le resultat est celui que l’on souhaite.Remarque sur les classes enveloppantes : des programmes sont deja definis en

Java, permettant de manipuler des references sur des scalaires (comme Intvalque l’on a defini plus haut), par exemple Integer (mais ceci n’est pas utilisableici car Integer est non-mutable).

2.15 Un peu de syntaxe

Terminons en recapitulant l’articulation d’un fragment purement imperatifen Java, C et Caml :

En Java, on ecrirait des definitions de fonctions avec leurs types (on reviendraa static au chapitre 4) :

stat ic T f (T1 x1 , . . . , Tn xn ) p

En C de meme :

T f (T1 x1 , . . . , Tn xn ) pint f ( const int x , int y ) return x+1;

Et enfin en Caml :

let f x1 . . . xn = t in plet hypothenuse x y = sq r t ( x ∗ . x +. y ∗ . y )

Les arguments sont alors toujours finaux.Un programme complet Java ressemblera, par sa structure, a :

class Prog stat ic T1 x1 = t1 ; . . .stat ic Tn xn = tn ;stat ic . . . f 1 ( . . . ) . . .stat ic . . . fp ( . . . ) . . .

public stat ic void main ( St r ing [ ] a rgs ) . . .

En C :

T1 x1=t1 ;. . .Tn xn=tn ;. . . f 1 ( . . . ) . . .. . .. . . fp ( . . . ) . . .

Page 33: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

2.15. UN PEU DE SYNTAXE 33

int main ( int argc , char ∗∗ argv ) . . .

Et en Caml :

(∗ v a r i a b l e s g l o b a l e s ∗)let x = . . . ; ;(∗ v a r i a b l e s l o c a l e s ∗)let x = . . . in . . . ; ;(∗ f on c t i on s − non r e c u r s i v e s ∗)let f arg1 . . . argn = corps de f onc t i on ; ;

Le main est une fonction speciale : c’est celle qui est le point d’entree al’execution. En argument, elle contient tous les arguments passes a la ligne decommande.

Examinons un instant la semantique du main a travers un exemple, ici enJava :

class Essa i public stat ic void main ( St r ing [ ] a rgs )

for ( int i =0; i<args . l ength ; i=i +1)System . out . p r i n t l n ( ”args [ ”+i+”]=”+args [ i ] ) ;

Ou encore de facon equivalente, en C :

int main ( int argc , char ∗∗ argv ) int i ;for ( i =0; i<argc ; i=i +1) p r i n t f ( ”argv [%d]=%s \n” , i , argv [ i ] ) ;return 0 ;

(code d’erreur eventuelle, retournee par la fonction main)La compilation et l’execution de ce code Java donnent :

> javac e s s a i . java> java Essa i premierarg deuxiemeargargs [0 ]= premierargargs [1 ]= deuxiemearg

Et en C :

> gcc e s s a i . c −o e s s a i> . / e s s a i premierarg deuxiemeargarg [0 ]= e s s a iarg [1 ]= premierargarg [2 ]= deuxiemearg

Page 34: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

34 CHAPITRE 2. PROGRAMMATION IMPERATIVE

Page 35: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Chapitre 3

Structures de donnees

3.1 Types produits, ou enregistrements

On a besoin de structurer les programmes, mais aussi les donnees. Prenonsl’exemple d’un codage de points par des coordonnees cartesiennes. Soit p unpoint de l’espace N3. On peut le representer par le triplet de ses coordonnees(x, y, z), x, y et z etant un entier. En vocable informatique : c’est un cas parti-culier de structure ou enregistrement, avec 3 champs entiers x, y, et z.

En Java, ce type de donnees se definirait comme suit :

class Point int x ;int y ;int z ;

Et en C :

struct Point int x ;int y ;int z ; ;

Et enfin en Caml :

type Point = mutable x : i n t ;mutable y : i n t ;mutable z : i n t ;

Ceci est un cas particulier de type produit, que nous developpons maintenant.Un produit (ou une puissance ici) dans les ensembles, comme N3 = N×N×N,

est la donnee d’un ensemble (ici de triplets) et de projections sur chaque coor-donnee, ainsi qu’une fonction permettant de construire un triplet a partir des 3coordonnees. C’est la vision plus algebrique, venant de la theorie des categories,d’un produit, et plus conforme a la vision que l’on aura des produits, dans les

35

Page 36: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

36 CHAPITRE 3. STRUCTURES DE DONNEES

langages de programmation. De fait Caml veut dire ”Categorical Abstract Ma-chine Language”et la machine abstraite d’execution d’origine etait basee sur des“combinateurs categoriques”. Ces proprietes (“universelles”) sont les suivantes :

X

X × Y

Y

Z

πX

∀f

πY

∀g

∃!h

Propriete universelle : ∀f, g, ∃!h, tel que

πX h = fπY h = g

Dans les ensembles– X × Y = (x, y) | x ∈ X, y ∈ Y – πX(x, y) = x, πY (x, y) = y (selec-

teurs)– h(z) = (f(z), g(z)) (constructeur)

En Java, on peut construire un nouveau point dans N3 a partir du construc-teur par defaut :

Point p=new Point ( ) ;p . x = 1 ;p . y = 2 ;p . z = 3 ;

Les projections sont les suivantes en Java :

int xx = p . x ;int yy = p . y ;int zz = p . z ;

En C et Caml on peut faire de meme, avec une syntaxe legerement differente :

struct point p = 0 , 2 , 3 ;p . x = 1 ;

(construction)

int xx = p . x ;int yy = p . y ;int zz = p . z ;

(projections)

let p = r e f x = 0 ; y = 2 ; z = 2 ; ; ;! p . x <− 1 ; ;

(construction)

let xx = ! p . x ; ;let yy = ! p . y ; ;let zz = ! p . z ; ;

(projections)

Page 37: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

3.2. LE RAMASSE-MIETTE, OU GC 37

Bien sur, en Java, on peut definir comme on l’a deja vu au chapitre 2, unconstructeur prenant directement en argument trois entiers, pour creer un point,qui alloue et affecte en meme temps un point (en fait, on peut definir autant deconstructeurs que l’on souhaite) :

class Point int x ; int y ; int z ;Point ( int a , int b , int c )

x = a ; // ou t h i s . x = a ;y = b ; // ou t h i s . y = b ;z = c ; // ou t h i s . z = c ;

Pour utiliser le constructeur on ecrit par exemple :

Point x = new Point ( 1 , 2 , 3 ) ;

En C et Caml il n’y a pas vraiment d’analogue direct, sauf en OCaml dansla surcouche orientee objet du langage fonctionnel, on en verra un peu plus auchapitre 4. Il y a donc ici un certain nombre de differences entre Java, C, Caml,pour le type Point. En Java il y a des constructeurs, une valeur vide (null) etdes valeurs par defaut a la creation d’un type produit (ou enregistrement). EnC, il n’y a pas de constructeur, mais il y a une valeur vide (null) et pas toujoursde valeurs par defaut. En Caml, il n’y a pas de definition de constructeur, pasde null, et pas de valeur par defaut.

3.2 Le ramasse-miette, ou GC

On a vu comment allouer, par new, des nouvelles cases memoires, contenantdes donnees structurees. En Java, comme en OCaml, on n’a pas a se soucierde la liberation de la memoire non utilisee. Il y a un mecanisme de garbagecollector ou glaneur de cellules, ou encore ramasse-miette, disponible pendantl’execution de tout programme.

Ce concept a ete invente par John MacCarthy pour le LISP (prix Turing1971). Il permet de recuperer la memoire non utilisee, c’est un processus quis’execute en parallele et automatiquement :

– determine quels objets ne peuvent plus etre utilises par un programme– recupere cet espace memoire (pour etre utilise lors d’allocations futures)

De nombreux algorithmes ont ete etudies, et implementes par exemple dansJava, Caml, mais pas dans C.

Les principes de fonctionnement sont les suivants. Pour des algorithmes detype “Mark and Sweep”, le GC commence a parcourir les locations memoiresvivantes (accessibles depuis les racines, i.e. les noms de variables du programmeJava). Pendant ce temps, l’execution du programme Java est suspendue. Il y aalors 2 phases :

– (mark) : Les objets alloues et visitables par le GC depuis les racines sonttaggues : visite et pas visite

Page 38: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

38 CHAPITRE 3. STRUCTURES DE DONNEES

– (sweep) : Le GC parcourt adresse par adresse le tas (l’endroit en memoireou sont alloues les objets) et “efface” les objets non taggues “visite”

Un autre type d’algorithme couramment utilise (eventuellement de facon adhoc par des programmeurs C qui doivent implementer un tel mecanisme dansleur programme) est le comptage de references. Le GC maintient avec chaqueobjet, un nombre de references pointant sur chaque objet. Si ce compteur arrivea zero, l’objet est libere. D’autres types d’algorithmes existent, que nous ne de-crirons pas : “stop and copy”, les GC conservatifs, incrementaux, generationnels(cas de Java) etc.

Comment fait-on alors dans d’autres langages, comme le C, qui n’ont pasde GC ? Il faut proceder a une allocation et deallocation manuelles. Voici unexemple sur les listes :

L i s t cons ( int car , L i s t cdr ) /∗ a l l o c a t i o n ∗/L i s t r e s = ( L i s t ) mal loc ( s izeof ( struct s t L i s t ) ) ;res−>hd = car ; res−>t l = cdr ; return r e s ;

void f r e e l i s t ( L i s t l ) i f ( l == nu l l ) return ;f r e e l i s t ( l−>t l ) ;/∗ d e a l l o c a t i o n ∗/f r e e ( l ) ;

Le programmeur a ainsi du ecrire le code d’une fonction freelist, qui valiberer, une par une, les cellules memoire d’une liste, par l’instruction free.Il devra dans son code reflechir precisement a quand appeler cette fonction,quoi partager en memoire etc. C’est la cause de nombreuses erreurs, car si onn’appelle pas assez les fonctions de liberation memoire, on court le risque de nepas avoir assez de memoire pour executer son programme (“fuite memoire”), etsi on contraire on libere trop, on va manipuler des adresses memoires invalides.

3.3 Enregistrements et appels de fonctions

Interessons nous au code Java suivant :

class Point int x ; int y ; int z ;

Point ( int u , int v , int w) x = u ; y = v ; z = w;

class Essa i stat ic void show ( Point p) System . out . p r i n t l n (p+”=(”+p . x+” , ”

+p . y+” , ”+p . z+”) ” ) ;

stat ic void i n c r ( Point p , Point q ) q . x += p . x ; q . y += p . y ; q . z += p . z ;

Page 39: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

3.4. LES TABLEAUX 39

public stat ic void main ( St r ing [ ] a rgs ) Point x0=new Point ( 1 , 2 , 3 ) ;Point x1=new Point (−1 ,−2 ,−3);i n c r ( x0 , x1 ) ;show ( x0 ) ; show ( x1 ) ;

Son execution donne :

> java PointPoint@70329f3d =(1 . 0 , 2 . 0 , 3 . 0 )Point@59bca5f1 =(0 . 0 , 0 . 0 , 0 . 0 )

On voit bien qu’il y a la comme un passage par reference. En fait, c’esttoujours la meme chose, les appels de fonction se font toujours par appel parvaleur, mais la valeur d’un “objet” de type Point est son adresse en memoire.

3.4 Les tableaux

Les tableaux sont un cas particulier de type produit, dont tous les champssont de meme type (cela serait plutot un type “puissance”). C’est en fait un peul’ancetre des types produits, qui est utilise par exemple pour coder les vecteurset matrices. Il y a la possibilite egalement, par rapport au chapitre 2, de fairedes tableaux 2D (matrices), 3D etc. (tenseurs)

On rappelle qu’en Java la declaration d’un tableau 1D se fait de la faconsuivante :

double [ ] x ;

En C, un tableau est la meme chose qu’un pointeur sur son premier element :

double ∗x ;

L’allocation (1D) se fait en Java comme suit :

x = new double [ 1 0 0 ] ;

Les indices du tableau ainsi alloue demarrent en 0 ; le dernier indice valideest 99.

En C :

x = (double ∗) mal loc (100∗ s izeof (double ) ) ;

ou alors plus simplement, a la declaration :

double x [ 1 0 0 ] ;

Les indices du tableau demarrent egalement en 0 et le dernier indice valideest aussi 99.

En Caml :

let x = Array . make 100 0 ; ;

Page 40: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

40 CHAPITRE 3. STRUCTURES DE DONNEES

Ici, tous les elements sont initialises a zero.Les projections, et constructions pour les tableaux 1D, vus comme types

produit sont : en Java et C :

double y = x [ 5 0 ] ;x [ 5 0 ] = 1 . 0 ;

Et en Caml :

let y=x . ( 5 0 ) ; ;x . (50) < −1.0 ; ;

Donnons un exemple de code utilisant les tableaux 1D, un simple calcul deproduit scalaire :

class vectn double [ ] comp ;

vectn ( int n) comp = new double [ n ] ;

class Essa i

stat ic double scproduct ( vectn p , vectn q ) double r e s = 0 ;for ( int i =0; i<p . l ength ; i++)

r e s = r e s+p . comp [ i ]∗ q . comp [ i ]return r e s ;

. . .

Et en C :

double u [ 1 0 0 ] , v [ 1 0 0 ] ; . . .double scproduct (double p [ 1 0 0 ] , double q [ 1 0 0 ] )

double r e s = 0 ;for ( int i =0; i <100; i++)

r e s = r e s+p [ i ]∗ q [ i ]return r e s ;

. . . s = scproduct (u , v ) ;

Ou (taille de tableau passe en parametre) :

double ∗u , ∗v ; int d ; . . .u = (double ∗) mal loc (d∗ s izeof (double ) ) ;v = (double ∗) mal loc (d∗ s izeof (double ) ) ; . . .

double scproduct (double ∗p , double ∗q , int dim) double r e s = 0 ;for ( int i =0; i<dim ; i++)

r e s = r e s+p [ i ]∗ q [ i ]return r e s ;

. . . s = scproduct (u , v , d ) ;

Page 41: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

3.4. LES TABLEAUX 41

Et finalement, en Caml :

let scproduct u v n = let r e s=r e f 0 .and n=Array . l ength u in

for i=0 to n dor e s := ! r e s +. u . ( i ) ∗ . v . ( i ) ;

done ;! r e s ; ;

Donne une fonction :

va l scproduct : f l o a t array −> f l o a t array −> f l o a t = <fun>

Illustrons maintenant la construction et la manipulation de tableaux 2D. Onconsidere une matrice de RN×M , avec N = 3 colonnes de M = 4 lignes :

En Java :

double M = new double [ 4 ] [ 3 ] ;M[ 0 ] [ 0 ] = 8 ;M[ 0 ] [ 1 ] = 1 ;. . .

8 1 6

3 5 7

4 9 2

10 12 11

Ou de facon plus compacte :

double [ ] [ ] M=8 ,1 ,6 ,3 ,5 ,7 ,4 ,9 ,2 ,10 ,12 ,11 ;

8 1 6

3 5 7

4 9 2

10 12 11

En C, il faut etre plus explicite sur le choix de l’organisation en memoire. Icion fait le choix d’implementer la matrice dans un bloc contigu en memoire enremarquant qu’une matrice N ∗M est donnee par un ensemble de N ×M reels.

On procede alors a l’allocation en faisant :float *x = (float *) malloc(N*M*sizeof(float)) ;On recupere xi,j par x[i*M+j].

8 1 6 3 5 7 4 9 2 10 12 11

On peut aussi choisir d’organiser la matrice en vecteur de vecteurs en remar-quant qu’une matrice N ∗M est un element de RN×M , c’est aussi un vecteur aN composantes chacune dans RM . L’allocation se fait alors par :

float **x = (float **) malloc(N*sizeof(float *)) ;Puis :

for ( i =0; i<N; i++)x [ i ] = ( f loat ∗) mal loc (M∗ s izeof ( f loat ) ) ;

Page 42: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

42 CHAPITRE 3. STRUCTURES DE DONNEES

Le tableau est ainsi implemente comme N pointeurs sur N vecteurs de tailleM (chacun un bloc contigu de memoire).

8 1 6

3 5 7

4 9 2

10 12 11

Tout ceci se generalise en dimension superieure bien sur.En Caml, on peut construire plus simplement les tableaux 2D :

let x = Array . make matrix n m 0 ; ;x . ( 1 ) . ( 2 ) < −9 . 0 ; ;

3.5 Egalite physique et egalite structurelle

Reprenons la classe Point et p = (1, 2, 3) :

int xx = p . x ;int yy = p . y ;int zz = p . z ;Point q = new Point (xx , yy , zz ) ;

p et q denotent les memes points (memes coordonnees, i.e. les champs ontmeme valeur), mais ne sont pas egaux (en tant qu’objets, c’est-a-dire de poin-teurs, ou de locations memoire) :

p q

x : 1 | y : 2 | z : 3 x : 1 | y : 2 | z : 3

Le test d’egalite physique p == q est donc faux, aussi bien en Java, C qu’enCaml, car p et q sont deux references distinctes :

p q

x : 1 | y : 2 | z : 3 x : 1 | y : 2 | z : 3

Page 43: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

3.6. PARTAGE 43

Par contre, l’egalite structurelle est elle vraie, c’est-a-dire que p et q denotentles memes points. On peut programmer cette egalite structurelle de la faconsuivante, en Java :

stat ic boolean equal ( Point p , Point q ) return (p . x==q . x)&&(p . y==q . y)&&(p . z==q . z ) ;

En C :

int equal ( struct Point p , struct Point q ) return (p . x == q . x ) && (p . y == q . y ) && (p . z == q . z ) ;

Et en Caml :

let equal p q = (p . x==q . x ) && (p . y==q . y ) && (p . z==q . z ) ; ;

(en fait, ceci est deja implemente en Caml par p=q, il n’est pas besoin de lereprogrammer dans ce cas)

3.6 Partage

L’egalite structurelle est couteuse : on peut parfois, par construction, l’obte-nir par l’egalite physique. L’idee pour les Point est de remplacer le new par unefonction de creation qui teste si un point de memes coordonnees existe deja, etne l’alloue pas de nouveau si c’est le cas. Pour ce faire, il faut un test d’egalitestructurelle... On l’approxime par un moyen peu couteux, le hachage et les tablesde hachage.

Le principe du hachage est de permettre a moindre cout, de distinguer a coupsur deux objets. Traditionnellement, cela est fait par une fonction de hachage,h : O → N qui associe a tout objet que l’on veut considerer, un entier, de tellefacon que h(x) 6= h(y) =⇒ x 6= y. Generalement, h est a valeur dans 0, . . . , n(pour un certain n ∈ N), on peut ainsi ranger les objets de O dans une tablede collision : tableau qui a i ∈ 0, . . . , n associe une liste de collision : tous les xtels que h(x) = i. On en verra une implementation un peu meilleure a la section3.8, quand on aura introduit les types de donnees recursifs et les listes.

Par exemple, pour Point on peut utiliser une fonction de hachage modulairesimple :

class Point . . .

class Hachage f ina l int n = . . . ; // nombre f i x ef ina l int m = . . . ; // t a i l l e f i x e e

stat ic int hache ( Point p) return (p . x+p . y+p . z)%n ;

Page 44: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

44 CHAPITRE 3. STRUCTURES DE DONNEES

On va maintenant gerer un ensemble de n points en se constituant une basede donnees de m (fixe) points distincts au maximum. Mais le remplissage de labase de donnees peut demander plusieurs fois d’inclure un point avec les memescoordonnees qu’un existant dans la base. On veut decider de l’egalite structurellerapidement (par l’egalite physique)

On va definir un tableau 2D - tab[i][j] est le jieme Point de code dehachage egal a i - comme suit :

class Tablenaive stat ic Point [ ] [ ] tab ;stat ic int [ ] f r e e i nd ex ;public stat ic void main ( St r ing [ ] a rgs )

tab = new Point [ n ] [m] ;f r e e i nd ex = new int [ n ] ; // tou t a zero. . .

Et on peut maintenant ecrire le code de creation non-redondante d’un Point :

class Tablenaive . . .stat ic void newPoint ( int x , int y , int z )

int k = hache (x , y , z ) ;Point p = new Point (x , y , z ) ;boolean found = fa l se ;for ( int i =0; i<f r e e i nd ex [ k ] ; i=i +1)

i f ( equal ( tab [ k ] [ i ] , p ) ) i=f r e e i nd ex [ k ] ; found=true ;

i f ( ! found ) tab [ k ] [ f r e e i nd ex [ k ] ]=p ;f r e e i nd ex [ k]= f r e e i nd ex [ k ]+1;

. . .

En voici un exemple, detaille pas a pas :On rajoute d’abord le point (0,0,0) (hachage=0) :

f ina l int n=3;f ina l int m=4;. . .newPoint (0 , 0 , 0 ) ;

(0,0,0) null null null

null null null null

null null null null

Puis le point (1,2,4) (hachage=1) :

newPoint (1 , 2 , 4 ) ;

(0,0,0) null null null

(1,2,4) null null null

null null null null

Puis le point (1,2,3) (hachage=0) :

Page 45: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

3.7. TYPES SOMME 45

newPoint (1 , 2 , 3 ) ;

(0,0,0) (1,2,3) null null

(1,2,4) null null null

null null null null

Et le point (2,3,6) (hachage=2) :

newPoint (2 , 3 , 6 ) ;

(0,0,0) (1,2,3) null null

(1,2,4) null null null

(2,3,6) null null null

Puis de nouveau le point (1,2,3) (hachage=0) :

newPoint (1 , 2 , 3 ) ;

(0,0,0) (1,2,3) null null

(1,2,4) null null null

(2,3,6) null null null

Comme on le voit, les points positifs de cette construction sont qu’il n’y apas de nouvelle creation de point dans la table pour (1,2,3), et ceci en deuxcomparaisons avec un autre point (au lieu de potentiellement 4). Le point ne-gatif est qu’il y a un cout memoire incompressible avec le tableau 2D : m × nelements alloues pour seulement m points possibles. Pour ameliorer cela, il nousfaudra utiliser des structures de donnees dynamiques (listes) a la section 3.8. Larecherche dans la table des collisions est egalement peu optimisee. On pourraitau moins ordonner les points, pour une recherche dichotomique.

3.7 Types somme

Prenons l’exemple d’un cas “primitif”. Supposons que l’on veuille representerun nombre fini de valeurs, par exemple, les couleurs d’une carte a jouer (ici enCaml) :

> type cou l eur = PIQUE | COEUR | CARREAU | TREFLE ; ;> let x = PIQUE ; ;va l x : cou l eur = PIQUE

Ceci est un cas simple de type somme/disjonctif. En Java :

enum cou l eur PIQUE, COEUR, CARREAU, TREFLE ;cou l eur c = cou l eur .PIQUE;System . out . p r i n t l n ( c ) ;

(donne PIQUE)Et en C :

enum cou l eur PIQUE, COEUR, CARREAU, TREFLE ;enum cou l eur c = PIQUE;

Page 46: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

46 CHAPITRE 3. STRUCTURES DE DONNEES

En fait, les types somme, ou disjonctifs, sont le dual des types produits. Unesomme, ou une disjonction, une union sont une forme de coproduit (au sens dela theorie des categories). Elle verifie la propriete universelle suivante, qui estla meme que celle sur les produits, mis a part le fait que toutes les fleches sontdans l’ordre inverse :

X

X∐Y

Y

Z

inX

∀f

inY

∀g

∃!h

Propriete universelle : ∀f, g, ∃!h, tel que

h inX = fh inY = g

Dans les ensembles– X

∐Y = (0, x) | x ∈ X ∪ (1, y) | y ∈

Y (union disjointe)– inX(x) = (0, x), inY (y) = (1, y)

– h(z) =f(x) if z = (0, x)g(y) if z = (1, y)

En fait, en Java (et en Caml) couleur.PIQUE (resp. PIQUE) est l’injec-tion canonique de l’ensemble a un element (PIQUE) vers le type enum couleur(resp. couleur). En C, ce n’est qu’une facon de nommer symboliquement desconstantes scalaires :

enum cou l eur PIQUE=0, COEUR=1, CARREAU=2, TREFLE=3 ;enum cou l eur c = PIQUE; // vaut 0 ( i n t )

Passons maintenant au jeu de cartes.En Caml :

> type ca r t e = As of cou l eur| Roi of cou l eur| Dame of cou l eur| Valet of cou l eur| Pe t i t e c a r t e of i n t ∗ cou l eur ; ;

> let x = As(PIQUE ) ; ;va l x : c a r t e = As PIQUE> let y = Pe t i t e c a r t e (8 , TREFLE) ; ;va l y : c a r t e = Pe t i t e c a r t e (8 , TREFLE)

As represente l’injection canonique d’une copie de couleur (“tagge” par As)vers le type carte. Le pattern-matching permet de programmer apres, sur cetype, de facon elegante.

On pourrait aussi ecrire en Caml quelque chose de theoriquement isomorphe(mais que le compilateur ne verra pas comme isomorphe)= :

> type va l eur = As | Roi | Dame | Valet | Pe t i t e c a r t e of i n t ; ;type va l eur = As | Roi | Dame | Valet | Pe t i t e c a r t e of i n t> type c a r t e b i s = va l eur ∗ cou l eur ; ;type c a r t e b i s = va l eur ∗ cou l eur> let z = (As , PIQUE ) ; ;

Page 47: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

3.7. TYPES SOMME 47

va l z : va l eur ∗ cou l eur = (As , PIQUE)

(valeur×couleur∼carte∼cartebis...)En Java, le type somme simple pour les valeurs “figures” (roi, dame, valet,

as) que l’on a defini en Caml, est implemente par un type enumere, par contre,on utilise un codage luxueux de la somme comme un produit cartesien a causedes valeurs “numeriques” :

class Carte enum va leur As , Roi , Dame, Valet , Pet i t eCarte ;enum cou l eur PIQUE, COEUR, CARREAU, TREFLE ;va l eur va l ; int v a l p e t i t e ;cou l eur c ;

On reconnaıt bien les enregistrements pour le produit valeur×couleur,mais valpetite n’est utile que quand val vaut PetiteCarte.

On a le meme probleme en C :

enum cou l eur Pique , Coeur , Carreau , T r e f l e ;enum va l eur As , Roi , Dame, Valet , Pet i t eCarte ;struct ca r t e

enum va l eur va l ; int v a l p e t i t e ;enum cou l eur c ; ;

Encore qu’en C, il y a un type somme primitif, les unions :

enum cou l eur Pique , Coeur , Carreau , T r e f l e ;union va l b i s

enum va l e u rb i s As , Roi , Dame, Valet value ;int v a l p e t i t e b i s ; ;

struct ca r t e union va l b i s va l ;enum cou l eur c ; ;

Le probleme, qui en est l’avantage egalement en fait : les espaces memoiresalloues aux differents champs d’une union sont les memes : x.val.value etx.val.valpetitebis sont les memes mots memoire. C’est une cause d’erreursfrequentes, ou une source d’inspiration pour des codage abscons.

Comme on l’a deja signale, la facon elegante de programmer sur les differentscas d’un type somme se fait en Caml par pattern-matching :

let f x = function| As −> . . .| Roi −> . . .. . .| Pe t i t e c a r t e (x , c ) −> . . .| −> . . . ; ;

Vous reconnaitrez exactement la fonction h de la propriete universelle defi-nissant le type produit.

En Java on a une forme “primitive” du selecteur :

Page 48: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

48 CHAPITRE 3. STRUCTURES DE DONNEES

stat ic f ( Carte x ) switch ( x . va l )

case As : . . . ; break ;case Roi : . . . ; break ;. . .default : . . . . . .

de meme en C.

3.8 Types de donnees dynamiques

Un cas classique ou l’on souhaite disposer d’un type de donnees“dynamique”,c’est-a-dire non entierement defini de facon statique, est celui ou l’on souhaitemanipuler des matrices dont la taille est inconnue au moment de l’ecriture ducode, et est parametrable au moment de l’execution.

On veut meme souvent des types dont la taille evolue au cours du temps :les tableaux ne sont alors plus pertinents. Reprenons l’exemple des tables dehachage, implementees precedemment par un tableau 2D - tab[i][j] est lejieme Point de code de hachage egal a i :

class Tablenaive stat ic Point [ ] [ ] tab ;stat ic int [ ] f r e e i nd ex ;public stat ic void main ( St r ing [ ] a rgs )

tab = new Point [ n ] [m] ;f r e e i nd ex = new Point [ n ] ; // tou t a zero

On va definir une liste l (de int par exemple) qui est une structure de donneestelle que :

– son premier element (car de LISP) est un int– son deuxieme element (cdr de LISP) est une liste d’entiers

C’est en quelque sorte un type produit, avec un nombre de produits non de-termine a l’avance. L’ecriture de ce type est sous la forme d’un type produit,recursif.

l car : . . . | cdr . . . | . . .

On a en effet envie d’ecrire, une sorte d”’equation aux domaines” :

ListN = () ∪ N× ListN

pour ne pas oublier la liste vide. Il s’agit donc d’un melange a priori de definitionde type somme et type produit, recursif.

Son implementation en Java est la suivante :

Page 49: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

3.8. TYPES DE DONNEES DYNAMIQUES 49

class L i s t int hd ;L i s t t l ;

En C :

typedef struct s t L i s t int hd ;struct s t L i s t ∗ t l ;

∗ L i s t ;

On remarque tout de suite qu’on n’a pas de codage particulier pour la somme.En fait, une liste non vide est allouee quelque part en memoire (pour conserverla valeur de hd et de tl), donc est un pointeur valide, different de null. Onidentifie la liste vide avec null, en Java et C.

En Caml, on n’a pas de pointeur null, donc on ecrit explicitement le typesomme, pour implementer les listes :

type l i s t e = Ni l | Cons of i n t ∗ l i s t e

Dans ce code, Nil est l’injection canonique de unit vers liste, et Cons estl’autre injection canonique de int*liste vers liste.

On peut alors ecrire directement :

> Cons (1 , Ni l ) ; ;− : l i s t e = Cons (1 , Ni l )

En Java, on aura un constructeur par defaut, lie a ce type, utilise par exempledans le code suivant :

L i s t l ;l = new L i s t ( ) ;l . hd = 4 ;l . t l = new L i s t ( ) ;l . t l . hd = 5 ;l . t l . t l = null ;

La construction de la liste se fera par etapes :Tout d’abord,

L i s t l ; l

Puis,

l = new L i s t ( ) ; l ? |?

Puis encore :

l . hd = 4 ; l 4 |?

Page 50: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

50 CHAPITRE 3. STRUCTURES DE DONNEES

Puis ensuite :

l . t l = new L i s t ( ) ; l 4 | . ? |?

Puis encore :

l . t l . hd = 5 ; l 4 | . 5 |?

Puis enfin :

l . t l . t l = null ; l 4 | . 5 | null

Il s’agit maintenant de definir l’injection canonique de N× List vers List :

class L i s t . . .L i s t ( int car , L i s t cdr )

hd = car ;t l = cdr ;

Du coup, on aurait pu ecrire (exemple precedent) List l = new List(4,new List(5, null)) ;

En C, on aurait pu ecrire une fonction assez similaire :

L i s t cons ( int car , L i s t cdr ) L i s t r e s = ( L i s t ) mal loc ( s izeof ( struct s t L i s t ) ) ;res−>hd = car ; res−>t l = cdr ; return r e s ;

Remarquez le res->hd, abbreviation de (*res).hd

3.9 Les listes lineaires

On appelle listes lineaires, les listes construites uniquement sans cycle. Dansce cas, les listes ont une notion de longueur : il suffit de definir length dansl’un des deux cas liste vide, liste non vide (donc egale a un cons(hd, l)) d’apresl’equation de domaines ListN = () ∪ N× ListN :

– length(()) = 0,– length(cons(hd, l)) = length(l) + 1Cela nous interdit bien sur des listes circulaires comme l = cons(0, l), qui

“represente” en quelque sorte la liste infinie constituee uniquement de zeros.

l 0 | .

En fait en Caml, tout cela est deja defini, les listes d’objets de type ’a :(type ’a list) :

Page 51: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

3.10. APPLICATION AUX TABLES DE HACHAGE... 51

> [ ] ; ;− : ’ a l i s t = [ ]> [ 1 ; 2 ; 3 ] ; ;> 1 : : 2 : : 3 : : [ ] ; ;− : i n t l i s t = [ 1 ; 2 ; 3 ]> [ 1 ; 2 ] @ [ 3 ; 4 ; 5 ] ; ;− : i n t l i s t = [ 1 ; 2 ; 3 ; 4 ; 5 ]> L i s t . l ength [ ”h e l l o ” ; ”world ” ; ” ! ” ] ; ;− : i n t = 3> L i s t . hd [ 1 ; 2 ; 3 ] ; ;− : i n t = 1> L i s t . t l [ 1 ; 2 ; 3 ] ; ;− : i n t l i s t = [ 2 ; 3 ]

3.10 Application aux tables de hachage...

A chaque i entre 0 et n, on va representer une liste de collisions possibles(au lieu d’un tableau bidimensionnel, fige) :

class Po i n t l i s t Point p ;P o i n t l i s t t l ;P o i n t l i s t ( Point q , P o i n t l i s t r )

p = q ; t l = r ;

class Table stat ic Po i n t l i s t [ ] tab ;public stat ic void main ( St r ing [ ] a rgs )

tab = new Po i n t l i s t [ n ] ;. . .

La creation d’un eventuel nouveau Point se fait comme suit :

stat ic Po i n t l i s t addPoint ( P o i n t l i s t l , Point q ) i f ( l == null )

return new Po i n t l i s t (q , null ) ;i f ( equal ( l . p , q ) )

return l ;else

return new Po i n t l i s t ( l . p , addPoint ( l . t l , q ) ) ;

stat ic void newPoint ( int x , int y , int z ) Point p = new Point (x , y , z ) ;int k = hache (p ) ;tab [ k ] = addPoint ( tab [ k ] , p ) ;

Page 52: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

52 CHAPITRE 3. STRUCTURES DE DONNEES

On peut alors derouler les ajouts suivants. On commence par ajouter le point(0,0,0) (hachage=0) :

newPoint (0 , 0 , 0 ) ;

tab[0] (0, 0, 0) | null

tab[1]

tab[2]

Puis le point (1,2,4) (hachage=1) :

newPoint (1 , 2 , 4 ) ;

tab[0] (0, 0, 0) | null

tab[1] (1, 2, 4) | null

tab[2]

Puis encore le point (1,2,3) (hachage=0) :

newPoint (1 , 2 , 3 ) ;

(0, 0, 0) | null GC !

tab[0] (0, 0, 0) | . (1, 2, 3) | null

tab[1] (1, 2, 4) | null

tab[2]

Et enfin le point (2,3,6) (hachage=2) :

Page 53: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

3.11. LISTES ET PARTAGE 53

newPoint (2 , 3 , 6 ) ;

tab[0] (0, 0, 0) | . (1, 2, 3) | null

tab[1] (1, 2, 4) | null

tab[2] (2, 3, 6) | null

En fait tout cela existe aussi en Java. On a les classes List, AbstractList,Vector, HashTable qui sont deja definis. De meme, en Caml : on a Hashtbletc.

3.11 Listes et partage

Les listes en pratique, font du partage, pour des questions d’efficacite (cf.hash-consing), et pour representer des structures de donnees “infinies”.

Commencons par l’idee de listes infinies (“rationnelles”). Par exemple, onveut representer toutes les listes infinies, ultimement periodiques, par exempleune liste

l = (0, (1, (2, (3, (2, (3, ....

(que des pattern 2 puis 3 repetes). On pourra ecrire par exemple :

L i s t l 3 = new L i s t ( ) ;l 3 . hd = 2 ;l 3 . t l = new L i s t (3 , l 3 ) ;L i s t l 2 = new L i s t (1 , l 3 ) ;L i s t l 1 = new L i s t (0 , l 2 ) ;

Remarque : c’est la construction correcte de l3 que l’on ecrit par “abus denotation” l3 = cons(2, cons(3, l3)). Il y a des langages (comme Haskell, voirchapitre 10) qui permettent de manipuler algorithmiquement les listes infinies,par evaluation paresseuse, que l’on peut egalement simuler en Caml, C ou Java.

Si on fait du partage de parties de listes, sans cycle, on economise justeen memoire, et cela peut permettre de faire du hash-consing sur les listes. Parexemple, pour la fonction append : on veut concatener une liste l2 au bout dela liste l1 :

stat ic L i s t append ( L i s t x , L i s t y ) i f ( x == null ) return y ;L i s t p = x ;while (p . t l != null ) p = p . t l ;p . t l = y ;return x ;

Considerons l’appel :

append ( l1 , l 3 ) ;

Page 54: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

54 CHAPITRE 3. STRUCTURES DE DONNEES

Avec :

l1 4 | . 5 | null

l3 2 | . 3 | null

Son execution procede ainsi :

x

l1 4 | . 5 | cnl1

p

l3 2 | . 3 | null

y

Puis,

x

l1 4 | . 5 | cnl1

p

l3 2 | . 3 | null

y

Et encore :

Page 55: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

3.11. LISTES ET PARTAGE 55

x

l1 4 | . 5 | cnl1

p

l3 2 | . 3 | null

y

On aurait pu encore ecrire cette version de append dans laquelle on n’a pasde partage, pour l’argument gauche :

stat ic L i s t append ( L i s t x , L i s t y ) i f ( x == null ) return y ;else

L i s t p = x ;L i s t q = new L i s t ( x . hd , null ) ;L i s t r = q ;while (p . t l != null )

q . t l = new L i s t (p . t l . hd , null ) ;q = q . t l ;p = p . t l ;

q . t l = y ;return r ;

On a en fait toutes les possibilites d’implementation : partage possible del’argument gauche, de l’argument droit, des deux, ou d’aucun des deux. L’interetou l’inconvenient des versions sans partage est que si l’on modifie en place leslistes l1, l2 ou l3, append(l1,l3) et append(l2,l3) ne sont pas modifiees.

Page 56: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

56 CHAPITRE 3. STRUCTURES DE DONNEES

Page 57: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Chapitre 4

Programmation orienteeobjet, en JAVA

Java est dans la lignee de langages de programmation “orientes objets” nom-breux, par exemple Simula 67 (base sur Algol 60), Smalltalk 71/80, ObjectiveC, C++ 83... L’utilite principale de l’approche orientee objet est essentiellementun style de programmation, qui permet de bien cloisonner le code, en unites co-herentes, cf. diagrammes de classes et UML (Unified Modelling Language). Celapermet aussi de reutiliser du code, plus aisement (“composants”), par heritageen particulier (voir section 4.3).

4.1 Statique versus dynamique

Jusqu’a present, on n’avait pas pu expliquer le mot cle class et on l’avaitutilise dans deux contextes apparemment tres differents. On avait defini desclass contenant des donnees (avec constructeurs neanmoins), ou les champsn’etaient pas static, pour les types produits, au chapitre 3. Ou alors, comme auchapitre 2, on avait defini des class ne contenant que du code, et des fonctionsdeclarees avec le mot cle static : les “programmes”. En fait, on peut meler lesdeux, et utiliser de facon generale des fonctions non static, ou “dynamiques”.

Une philosophie generale de la programmation orientee objet peut etre de-crite, en premiere approximation par l’exemple suivant qui decrit une file, constr-uite a partir d’une liste d’entiers :

class Pi l e L i s t c ;P i l e ( L i s t x ) this . c = x ;

class Prog stat ic void push ( int a , P i l e l )

l . c = new L i s t ( a , l . c ) ; stat ic void pop ( P i l e l )

57

Page 58: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

58 CHAPITRE 4. PROGRAMMATION ORIENTEE OBJET, EN JAVA

l . c = l . c . t l ; stat ic int top ( P i l e l )

return l . c . hd ;

D’une certaine facon, on voudrait mettre toutes les methodes concernant lespiles dans class Pile, pour en faire un “module” coherent et reutilisable. Onobtiendrait ainsi :

class Pi l e L i s t c ;P i l e ( L i s t x ) this . c = x ; stat ic void push ( int a , P i l e l )

l . c = new L i s t ( a , l . c ) ; stat ic void pop ( P i l e l )

l . c = l . c . t l ; stat ic int top ( P i l e l )

return l . c . hd ; class Prog

. . . P i l e . push (1 , p ) ; . . .

Les fonctions s’appliquant a la collection ou classe des piles sont comme deschamps fonctionnels d’un type enregistrement, on les appelle des methodes. Onlance leur execution en faisant Pile.methode.

Malgre tout, il reste une difference entre ces champs fonctionnels et le champde donnees List c. Quand on a une Pile p, on fait p.c pour obtenir son champde type List, pourquoi fait-on ici Pile.push(1,p) pour les methodes ? c estun champ non statique (pas de qualificatif static) alors que push est unemethode statique (qualificatif static). Commencons par expliquer les champsstatiques/non-statiques avant les methodes. Experimentons le code suivant :

class Stat stat ic int x = 2 ;

class Prog public stat ic void main ( St r ing [ ] a rgs ) Stat s , t ; s = new Stat ( ) ; t = new Stat ( ) ;System . out . p r i n t l n ( s . x ) ;t . x = 3 ;System . out . p r i n t l n ( s . x ) ;

Cela donne :

> java Prog23

Cela n’a pas l’air tres logique... Alors qu’en changeant juste le programmede la facon suivante :

class Stat2 int x = 2 ;

On obtient bien ce que l’on souhaite :

Page 59: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

4.1. STATIQUE VERSUS DYNAMIQUE 59

> java Prog22

En fait, une class (classe) definit un ensemble d’objets. Stat (version 1,statique) ne contient qu’un singleton alors que Stat2 (version 2, dynamique)peut contenir n’importe quel nombre d’objets, dont le trait commun est qu’ilscontiennent un champ entier x initialise a 2. Plus generalement, les champsstatic sont communs a tous les objets de cette classe.

Revenons aux methodes. D’une certaine maniere, l’appel Pile.push(1,p)est lourd pour pas grand chose. On voudrait ecrire comme pour les champs dedonnees quelque chose comme p.push(1). C’est possible avec une methode pushnon statique.

On obtient alors le code suivant :

class Pi l e L i s t c ;P i l e ( L i s t x ) this . c = x ;

. . .void push ( int a )

this . c = new L i s t ( a , this . c ) ;

void pop ( ) this . c = this . c . t l ;

int top ( ) return this . c . hd ;

On obtient ainsi un “module” coherent parlant de piles, avec toutes les ope-rations “legales” associees.

De meme que pour les champs de donnees statiques, les methodes static,sont toutes communes a leur classe (on parle alors de champ ou de methodede classe). C’est toujours le cas de main par exemple. Quand on programmeavec la version dynamique, p.push est une fonction differente de q.push (pourdeux piles p et q distinctes). push (dynamique) definit une fonction partielle,instanciee par le passage de p (appele this - objet courant sur lequel s’ap-plique la methode) lors de son appel par p.push(...) (regles de passage d’ar-gument inchangees). Quand on fait p.push(...), la machine regarde le type dep, voit Pile, trouve l”’enregistrement” (fonctionnel) push, passe la reference surle contenu de p a push puis execute son code.

Vous pouvez neanmoins avoir de bonnes raisons pour ecrire des methodesstatiques, par exemple, les fonctions de librairie Java Math.sin, Math.cos etc.

Remarques : this... est le plus souvent implicite. On aurait pu ecrire :

void push ( int a ) c = new L i s t ( a , l . c ) ;

void pop ( ) c = l . c . t l ;

Page 60: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

60 CHAPITRE 4. PROGRAMMATION ORIENTEE OBJET, EN JAVA

Autre remarque importante : le cas de null. Il ne faut jamais faire p.push(...)quand p vaut null, car c’est une valeur indefinie, qui n’a meme pas de type nidonc de pointeurs vers les champs ou methodes qu’on aimerait y associer.

4.2 Types somme, revisites

Tout cela rend possible une autre implementation des types somme (voir lechapitre 3).

Considerons le probleme de representer des expressions arithmetiques. Onconstruit un arbre syntaxique. Les expressions qui nous interessent sont de laforme :

expr = V ar | Cste | expr + expr | expr ∗ expr | −expr

Et on ecrit une classe Java les implementant, comme suit :

class Expr int s e l e c t ;int Cste ;S t r ing Var ;Typop Op;Expr gauche ;Expr d r o i t e ; . . .

enum Typop plus , minus , t imes

On inclut ici tout dans le type produit. On utilise select : si 0, l’expressionest une constante (champ Cste), si 1, l’expression est une variable (champ Var),si 2, l’expression est un operateur binaire (Op est plus ou times) et les sous-expressions sont gauche et droite, ou l’expression est unaire (Op est minus) etgauche est la sous-expression.

Neanmoins, on peut donner un style de programmation assez “propre” avecles constructeurs. Il est commode en effet de definir plusieurs constructeurs selonles cas (qui “imitent” les injections dans le type somme) :

Expr ( int constante ) s e l e c t = 0 ; Cste = constante ;

Expr ( St r ing va r i ab l e ) s e l e c t = 1 ; Var = va r i ab l e ;

Expr (Typop operateur , Expr arg l , Expr argr ) s e l e c t =2; Op=operateur ; gauche=a rg l ; d r o i t e=argr ;Expr (Expr arg )

s e l e c t = 2 ; Op = minus ; gauche = arg ;

Par exemple, une expression 2 ∗ x+ 1 est representee comme :

Expr e1 = new Expr ( 1 ) ;Expr e2 = new Expr ( 2 ) ;Expr e3 = new Expr ( ”x” ) ;Expr e4 = new Expr ( times , e2 , e3 ) ;Expr e5 = new Expr ( plus , e4 , e1 ) ;

+

×

2 x

1

Page 61: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

4.3. HERITAGE 61

Terminons cette section par quelques premiers elements de vocabulaire com-muns aux langages orientes objet :

– objet : structure de donnee compose de :– attributs : ou champs (cf. types produit, ou enregistrement !), ce sont les

donnees elementaires composant l’objet– methodes : fonctions pouvant s’appliquer a ce type d’objet

– classe : ensemble d’objets du meme type– instance : on dit qu’un objet est une instance de sa classeUn objet instance d’une classe, par exemple :

class Toto int champ1 ; int champ2 ;int methode1 ( int parametre ) . . .

Toto x = new Toto ( ) ; . . .

contient des champs de donnees et des methodes qui sont des fonctions s’appli-quant sur tout objet de la classe : x.methode1(parametre) (en C, non orienteobjet, on ecrirait methode1(x,parametre)).

Les methodes sont des fonctions associees a une classe d’objets. Elles peuventavoir plusieurs qualificatifs : methodes static, ou methodes de classe, on ne peutfaire que methode1(parametre) ou Toto.methode1(parametre). La methodeappelee n’a alors pas de connaissance d’un objet x particulier, mais juste dela classe Toto. Les methodes public sont connues de tout le monde. Il existecertaines methodes particulieres dites constructeurs (deja vues au chapitre 3).

4.3 Heritage

Commencons par un exemple : on a defini une classe Point et ses methodes,au chapitre 3 :

class Point int x , y ; // coordonneest r a n s l a t i o n ( int u , int v )

x = x+u ; y = y+v ; . . .

On veut maintenant des points colores ; au lieu de redefinir (a gauche), onpeut ecrire (a droite) :

class ColorPoint int x , y ; // coordonneesint c o l ; // cou l eurt r a n s l a t i o n ( int u , int v ) x = x+u ; y = y+v ; . . .

class ColorPointextends Point

int c o l ; // cou l eur. . .

Par le mot cle extends : class A extends B ... , on dit que A herite deB. Cela veut dire qu’un ColorPoint cp aura des champs col (accessible parcp.col), mais aussi x et y (accessibles par cp.x et cp.y). On aura aussi le faitque translation s’applique implicitement sur un objet de la classe ColorPointen s’appliquant a sa “sous-partie” Point.

Page 62: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

62 CHAPITRE 4. PROGRAMMATION ORIENTEE OBJET, EN JAVA

Cela s’appelle l’heritage, et permet une economie et une structuration ducode meilleure (en Java et Caml, n’existe pas en C “pur”).

Remarque : tous les objets Java heritent d’une classe unique : Object.La structuration par heritage se decrit generalement par des diagrammes

de classes, qui sont des graphes decrivant les attributs des classes, et leur re-lation d’heritage, comme dans l’exemple ci-dessous pour les classes Point etColorPoint :

class Point

class ColorPoint

On pourrait imaginer d’heriter de plusieurs classes en meme temps, pourpouvoir utiliser des champs et des methodes de diverses classes : cela s’appellel’heritage multiple, et est autorise en C++ mais pas en Java. L’heritage multiplede code est complique semantiquement, et le choix a ete fait en Java de ne pasl’autoriser, mais d’autoriser l’heritage simple de code, et multiple de signatures(voir section 4.5).

Terminons cette section par un resume du vocabulaire utile :– heritage : une classe peut etre une sous-classe d’une autre : la sous-classe

l’etend en rajoutant des methodes et des attributs, elle accede aux attributset aux methodes de sa sur-classe

– polymorphisme (par sous-typage) : un objet d’une sous-classe A de B enaccedant aux methodes de B est considere du type B (et A)

En fait, on a aussi une forme de polymorphisme pour les methodes :Prenons l’exemple de deux implementations possible de calcul garanti (c’est-

a-dire qui permet de “representer” fidelement le calcul dans les reels, en utilisantdes nombres machine, en precision finie), l’une par arithmetique rationnelle :

class Rat int p , q ;Rat ( int x , int y ) p=x ; q=y ; Rat p lus (Rat y )

return new Rat ( this . p∗y . q+this . q∗y . p ,this . q∗y . q ) ;

void show ( ) System . out . p r i n t l n (p+”/ ”+q ) ;

L’autre par arithmetique d’intervalles :

class Double int double i n f , sup ;Double int (double x , double y ) i n f=x ; sup=y ; ;Double int p lus ( Double int y )

return new Double int ( this . i n f+y . in f ,

Page 63: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

4.4. EXCEPTIONS 63

this . sup+y . sup ) ; void show ( )

System . out . p r i n t l n ( ” [ ”+i n f+” , ”+sup+” ] ” ) ;

plus et show sont polymorphes : peuvent prendre des Rat ou des Doubleint.Cela est pratique, on peut ne changer que la donnee et pas le programme.

En voici un exemple d’execution :

class Prog public stat ic void main ( St r ing [ ] a rgs )

Rat r = new Rat ( 1 , 2 ) ;Rat s = new Rat ( 1 , 3 ) ;Rat t = r . p lus ( s ) ; // = s . p l u s ( r ) ;Double int r i = new Double int ( 0 . 5 0 , 0 . 5 0 ) ;Double int s i = new Double int ( 0 . 3 3 , 0 . 3 4 ) ;Double int t i = r i . p lus ( s i ) ; // = s i . p l u s ( r i ) ;t . show ( ) ;t i . show ( ) ;

Cela donne :

5/6[0 .8300000000000001 ,0 .8400000000000001 ]

4.4 Exceptions

Voici un autre trait “moderne” de langages de programmation comme Java(et qui n’existe pas en C par exemple) : les exceptions, pour traiter des casd’erreur.

Reprenons le code de pop() dans la classe Pile :

void pop ( ) this . c = this . c . t l ;

Comment traiter le cas this.c == null ? :

void pop ( ) i f ( this . c != null )this . c = this . c . t l ;

Le probleme est que laisser this.c a null est trompeur, mais que faired’autre, renvoyer un code d’erreur : int pop()). C’est ce que l’on ferait en C,mais cela n’est pas tres satisfaisant de devoir rajouter un code de retour dansle resultat.

Si l’on fait :

Pi l e p = empty ( ) ;p . pop ( ) ;

Page 64: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

64 CHAPITRE 4. PROGRAMMATION ORIENTEE OBJET, EN JAVA

On obtient :

Exception in thread ”main ” java . lang . Nul lPo interExcept ionat Prog . pop ( P i l e . java : 1 6 )at Prog . main ( P i l e . java : 2 3 )

L’appel du programme a “leve une exception”, qui aurait pu etre “rattrapee”et traitee par l’appelant. Une exception est en quelque sorte le resultat d’uncomportement errone. Ce n’est pas vraiment comme une erreur au sens classiquedu terme : c’est un objet traitable par le programme. Tout calcul peut lancer(“throw”) une exception, c’est-a-dire retourner en plus d’une valeur, un typed’erreur, a son appelant, qui pourra la recuperer (“catch”) pour la traiter, ou larelancer a son appelant etc.

Ceci est a distinguer d’une erreur qui ne peut etre traitee et qui arrete leprogramme dans un etat potentiellement incoherent.

Les exceptions en Java forment un certain nombre de classes : Exception,avec pour sous-classes, IOException, RuntimeException etc.

Cela veut dire en particulier que l’on peut creer une nouvelle exception parheritage :

class Monexception extends Exception . . .

L’instanciation se fait par :

new Monexception ( ) ;

Quand une methode peut lancer une exception, il faut le declarer :

void pop ( ) throws Monexception . . .

Cela declare que pop() ne renvoie rien comme donnee, mais peut lancer uneexception, rattrapable par l’environnement.

Lancer une exception se fait de la maniere suivante. On ne doit pas fairereturn new Monexception() ;, mais plutot throw new Monexception() ; parexemple :

void pop ( ) throws Monexception i f ( this . c == null ) throw new Monexception ( ) ;this . c = this . c . t l ;

throw a une semantique un peu comme return, cela interrompt le code.Rattraper une exception se fait de la facon suivante :

try p . pop ( ) ; catch ( Monexception e ) System . out . p r i n t l n ( ”P i l e v ide ! ” ) ;. . . . . .

Les executions possibles sont alors :– Si p.c n’est pas null, alors p.pop() termine normalement, sans lever

d’exception ; le code se poursuit normalement au “...”

Page 65: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

4.5. INTERFACES 65

– Si p.c est null, alors p.pop() lance une exception, qui est rattrapee parcatch (Monexception e), e vaut alors l’objet de type Monexception creepar p.pop()

– On peut lancer de nouveau cette exception a l’appelant de l’appelant etainsi de suite.

4.5 Interfaces

Une interface est un ensemble de declarations de methodes sans implemen-tation. Cela est defini par le mot cle interface. Les interfaces permettent dedeclarer des variables avec le type de l’interface, mais elles ne sont pas instan-ciables, il n’y a pas de constructeur en particulier. Une classe peut implementerune ou plusieurs interfaces, par le mot cle implements, cela permet de faire del’heritage multiple en quelque sorte, mais seulement simple, de code.

Donnons-en un exemple, quasi fonctionnel, avec une interface pour les fonc-tions de N dans N :

interface Function public int apply ( int n ) ;

Il y aura donc une seule methode a implementer pour etre une fonction deN dans N : l’application apply.

En voici des exemples :

public class Carre implements Function public int apply ( int n)

return n∗n ;

public class Fact implements Function public int apply ( int n)

. . . return f a c t ;

public class Exemple public stat ic void main ( St r ing [ ] a rgs )

Carre x = new Carre ( ) ; Fact y = new Fact ( ) ;System . out . p r i n t l n ( ”Carre (3)= ”+x . apply ( ) ) ;System . out . p r i n t l n ( ”Fact (4)= ”+y . apply ( ) ) ;

4.6 Heritage et typage

On definit une relation de sous-typage comme suit. On note T ← S si S estun sous-type de T , defini par :

– T ← T– si la classe S est une sous-classe de T , on a T ← S– si l’interface S est une sous-interface de I, on a I ← S ;– si la classe C implemente l’interface I, on a I ← C

Page 66: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

66 CHAPITRE 4. PROGRAMMATION ORIENTEE OBJET, EN JAVA

– si T ← S, alors T []← S[]– si S ← SS et T ← S, alors T ← SSLa propriete fondamentale de cette relation est que si T ← S, alors toute

valeur du type S peut etre utilisee en lieu et place d’une valeur de type T(transtypage implicite). En voici un exemple. Si S sous-type de T , on peut fairele transtypage (cast) implicite :

S x = new S ( . . . ) ;T y = x ;

Dans l’autre sens c’est interdit (meme quand finalement les types sont assezsimilaires) :

class X f loat n ; class Y extends X

public stat ic void main ( St r ing [ ] a rgs ) X x = new X( ) ;Y y = x ;

A la compilation on obtient une erreur :

javac Y. javaY. java : 5 : incompat ib le typesfound : Xrequ i r ed : Y

Y y = x ; ˆ

1 e r r o r

Il faut faire dans ce cas un transtypage (cast) explicite :

class X f loat n ; class Y1 extends X

public stat ic void main ( St r ing [ ] a rgs ) X x = new X( ) ;Y1 y = (Y1) x ;

A la compilation tout se passe bien...mais le bon comportement dans cecadre depend entierement de l’utilisateur (ce qui est different du typage fort ala CAML, cf. chapitre 9).

En C le cast est d’emploi tres courant, par exemple a l’allocation (cf. chapitre3, sur l’allocation des listes en C) :

L i s t r e s = ( L i s t ) mal loc ( s izeof ( struct s t L i s t ) ) ;

car malloc renvoie le type fourre-tout void *...

4.7 Classes abstraites

Les classes abstraites sont une sorte d’intermediaire entre interfaces et classes(dites concretes). Elles permettent de definir des variables avec le type corres-pondant et permettent l’heritage (comme les classes concretes). Elles permettent

Page 67: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

4.8. PAQUETAGES 67

aussi de specifier des methodes abstraites avec l’attribut abstract, qui sont desspecifications de methodes, mais pas leur implementation (comme les interfaces).

En voici un exemple classique (Denis Monasse) qui permet de revenir a uneimplementation differente en Java des types somme (cf. chapitre 3) :

abstract class Carte public stat ic f ina l int PIQUE = 0 ,

COEUR = 1 , CARREAU = 3 , TREFLE = 4 ;int cou l eur ;

abstract int va l eur ( int cou l eu r a tou t ) ;

class As extends Carte int va l eur ( int cou l eu r a tou t ) return 11 ;

class Valet extends Carte int va l eur ( int cou l eu r a tou t )

i f ( cou l eur==cou l eu r a tou t ) return 20 ;else return 1 ;

. . .

4.8 Paquetages

Les paquetages permettent d’organiser un ensemble de classes et d’interfacesen un tout coherent (sorte de module a la CAML). Ils limitent en particulierla portee des identificateurs . Un paquetage peut avoir un nom, et des sous-paquetages (une arborescence, comme des repertoires UNIX).

En voici un exemple : l’API standard de JAVA est organisee en paquetage etsous-paquetages. Le paquetage java contient java.lang, java.io, java.utiletc.

Voici une facon de definir un paquetage :

package monPackage ;

public class A public methodeA ( ) . . . public class B public methodeB ( ) . . .

Et d’importer un paquetage :

import monPackage ;

. . .new A( ) . methodeA ( ) ;new B( ) . methodeB ( ) ;

On peut importer tous les classes d’un paquetage par :

import java . i o . ∗ ;

Remarques : java.lang est toujours importe automatiquement - toutes lesclasses definies jusqu’a present etaient dans le paquetage anonyme.

Page 68: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

68 CHAPITRE 4. PROGRAMMATION ORIENTEE OBJET, EN JAVA

4.9 Collections

Pour aller plus loin, sachez qu’il existe un mecanisme de collection Java, quipermet de representer divers types d’elements d’objets JAVA :

4.10 Les objets en (O)Caml

Pour etre complet, et pour les eleves qui ont deja une experience en OCaml,signalons les principales differences avec Java. Les enregistrements et objetssont les memes choses en Java, alors que les enregistrements et objets sontdifferents en Caml (la partie oriente objet est une sur-couche, venue longtempsapres). Seules les methodes peuvent acceder aux champs en Caml (pas visiblesautrement). Cela correspond en fait a des methodes private de Java, que l’on netraite pas dans ce cours.

Voici un exemple OCaml :

c l a s s p i l e = ob j e c tva l mutable c = ( [ ] : i n t l i s t )method ge t c ( ) = cmethod testempty ( ) = c =[ ]method push x = c<−x : : cmethod pop ( ) = c<−L i s t . t l cmethod top ( ) = L i s t . hd c

end

Page 69: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Chapitre 5

Recursivite, calculabilite etcomplexite

5.1 La recursivite dans les langages de program-mation

Le code suivant a t-il un sens ?

int ack ( int n , int p) i f (n==0) return p+1;i f (p==0) return ack (n−1 ,1) ;return ack (n−1, ack (n , p−1)) ;

Oui, ce code a un sens car la“suite d’appel”definissant ack termine toujours :ack(1,1)

ack(1,0) → xack(0,1) → 2x→ 2

ack(0,x) → 3→ 3

De facon generale, pour prouver la terminaison d’un processus recurrent ourecursif, il faut trouver un ordre sur les valeurs successives manipulees par lafonction recursive. Cet ordre est souvent associe a une valeur d’une fonction,decroissante a chaque appel, que l’on appelle variant, mais pas toujours. Icinous introduisons un ordre tres classique, l’ordre lexicographique, sur (n, p) lesdeux arguments entier de ack : l’ordre lexicographique sur N × N est definicomme suit :

(x, y) < (x′, y′) ssix < x′ ou,x = x′ et y < y′

Alors il suffit de verifier qu’a chaque appel de la fonction ack, les argumentssuccessifs (n, p) sont de plus en petits, au sens de l’ordre lexicographique que

69

Page 70: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

70 CHAPITRE 5. RECURSIVITE, CALCULABILITE ET COMPLEXITE

nous venons de definir. Nous examinons les trois cas possibles de la definitionde ack :

– ack(0, p) = p+ 1 : il n’y a pas d’appel recursif, la terminaison est acquise– ack(n+ 1, 0) = ack(n, 1) : (n, 1) < (n+ 1, 0) decroıt a l’appel suivant– ack(n + 1, p + 1) = ack(n, ack(n + 1, p)) : (n + 1, p) < (n + 1, p + 1) et

(n, ack(n+ 1, p)) < (n+ 1, p+ 1) decroissent aux appels suivants.Tout ceci sera defini plus formellement au Chapitre 6, pour l’instant, indi-

quons comment ceci est implemente et execute en machine.

5.2 L’execution de fonctions recursives, la piled’appel

De facon generale, on dit qu’une fonction est recursive si elle s’appelle elle-meme. Plus generalement, des fonctions sont dites mutuellement recursives si legraphe d’appel des fonctions est cyclique : f appelle g qui appelle h...qui appellef !

On peut se poser naturellement la question de l’utilite de la definition recur-sive de fonctions. Considerons le code Java suivant :

stat ic int i t e r ( int x ) while (x<1000)x = x+2;return x ;

Il itere sur les nombres entiers avec un pas de deux a chaque tour. On peutdefinir cette fonction de facon recursive :

stat ic int a c c i t e r ( int x , int acc ) i f (x>=1000) return acc ;return a c c i t e r ( x+2, acc +2);

stat ic int i t e r ( int x ) return a c c i t e r (x , x ) ;

Les boucles simples peuvent de facon generale s’exprimer comme des appelsrecursifs, parfois de facon plus naturelle, ce qui n’est certainement pas le cas ici.Mais surtout, les fonctions recursives permettent d’ecrire des fonctions qu’on nepourrait pas coder autrement.

Reprenons la fonction ack du debut de ce chapitre, dite fonction d’Acker-mann, du nom du mathematicien Ackermann :

ackermann (0 , p) = p+1ackermann (n+1 ,0) = ackermann (n , 1 )ackermann (n+1,p+1) = ackermann (n , ackermann (n+1,p ) )

n’est pas implementable par boucles, par contre, il se programme bien enJava, comme on l’a vu (en C) au debut de ce chapitre :

stat ic int ackermann ( int n , int m) i f (n==0) return m + 1;else i f (m==0) return ackermann (n−1 ,1) ;

Page 71: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

5.2. PILE D’APPEL 71

else return ackermann (n−1,ackermann (n ,m−1)) ;

De meme dans d’autres langages, en C ici :

int ackermann ( const int n , const int m) i f (n == 0) return m + 1;else i f (m == 0) return ackermann (n − 1 , 1 ) ;

else return ackermann (n − 1 , ackermann (n , m − 1 ) ) ;

Et en Caml :

let rec ackermann = function| (0 , n ) −> n + 1| (m, 0) −> ackermann (m − 1 , 1)| (m, n) −> ackermann (m − 1 , ackermann (m, n − 1 ) ) ; ;

Attention neanmoins aux definitions circulaires :

stat ic int f ( f ina l int x ) return f ( x ) ;

Ce code, qui ne termine pas, est repere a probleme par le compilateur JAVA(mais des exemples plus subtils ne le seront pas forcement) :

> javac toto . javatoto . java : 7 : cannot return a value from method whose r e s u l ttype i s void

return f ( 1 ) ;ˆ

1 e r r o r

Comme on le verra dans la semantique denotationnelle des boucles et de larecursivite au chapitre 6, une fonction recursive ne terminant pas est codee parune fonction qui n’a pas de valeur de retour (ou la valeur “indefinie”, ⊥).

Reprenons maintenant une definition de fonction recursive raisonnable, pourla fonction factorielle : fact(0)=1 et fact(n+1)=(n+1)*fact(n). Cette defini-tion recursive est en fait une simple definition par recurrence, que l’on peutcoder dans differents langages ainsi :

En Java :

stat ic int f a c t ( f ina l int x ) i f ( x == 0) return 1 ;return x∗ f a c t (x−1);

(attention tout de meme, et le compilateur n’est pas assez intelligent pourle voir, on n’a la terminaison que si x est positif)

En C :

int f a c t ( const int x ) i f ( x == 0) return 1 ;return x∗ f a c t (x−1);

Et enfin en Caml :

Page 72: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

72 CHAPITRE 5. RECURSIVITE, CALCULABILITE ET COMPLEXITE

let rec f a c t = function0 −> 1

| n −> n∗ f a c t (n−1) ; ;

Avant de definir la semantique des appels recursifs, donnons en une seman-tique informelle, proche de l’execution reelle du programme, dite semantique“par deroulement”. L’implementation d’un code recursif repose sur une pile d’ap-pel. Celle-ci permet aux appelants successifs de se souvenir du site d’appel (pourpouvoir revenir a l’execution dans l’appelant apres le return), et du contexted’appel, pour pouvoir retrouver les valeurs des variables locales a l’appelant,apres return.

Dans le code de la factorielle, il s’agit de la valeur de x de l’appelant et dela ligne d’appel, ici la ligne 3 :

1 stat ic int f a c t ( f ina l int x ) 2 i f ( x == 0) return 1 ;3 return x∗ f a c t (x−1);

Ainsi, lors de l’appel de fact(3), sont produit les appels successifs fact(2),fact(1) etc. qui empilent a chaque fois la valeur de x de l’appelant et l’adressede retour a l’appelant (la ligne 3) :

fact(3)

fact(2)

fact(1)

fact(0)

La pile d’appel contient a l’appel de fact(0) :

PC Ctxl.3 x=3l.3 x=2l.3 x=1

ou PC est le “Program Counter”, c’est-a-dir le numero de l’instruction a laquelleil va falloir revenir au retour de l’appel recursif, et Ctx est le “Contexte”, doncles valeurs des variables locales a l’appelant, qu’il faudra reprendre au retour del’appel recursif.

Au retour de l’appel a factorielle le plus profond (fact(0)), return depilela derniere valeur empilee, permettant au flot de controle de revenir a la bonne

Page 73: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

5.2. PILE D’APPEL 73

ligne de l’appelant (ligne 3) et avec la bonne valeur du contexte (x=1). Leprogramme termine quand la pile est vide, avec la valeur 6 ici.

Une question naturelle a se poser quand on programme de facon recursiveest de savoir si cela est couteux. En effet, l’execution repose sur une structure dedonnee supplementaire qui peut prendre potentiellement beaucoup de memoire.Par exemple ici, une execution de la factorielle avec une valeur initiale tropimportante resulte en une erreur (pas assez de place pour pouvoir empiler denouvelles valeurs sur la pile d’appel) :

> java Fact 1000000Exception in thread ”main ” java . lang . StackOverf lowError

at Fact . f a c t ( f a c t . java : 5 )at Fact . f a c t ( f a c t . java : 5 ). . .

Bien sur, cet exemple n’a que peu d’interet dans le sens ou la valeur quiserait obtenue serait de toutes facons tres superieure a ce qui est representabledans un type int.

Neanmoins, comme on le demontrera sous peu, il est des choses que l’on nepeut calculer qu’au moyen de definitions recursives, et pas calculables avec desboucles simples (qui n’ont pas besoin de structures de donnees supplementairespour etre executees).

Dans un certain nombre de cas, les definitions recursives peuvent se derecur-siver, c’est-a-dire etre transformees en boucles simples. Dans certains cas, celaest fait directement par le compilateur, par exemple dans le cas de la recursionterminale.

Par exemple, le code de la factorielle, recursif, peut s’ecrire de facon equiva-lente avec une boucle while, par exemple ici en Java :

stat ic int f a c t ( f ina l int x ) int i ;int r e s = 1 ;for ( i=x ; i >=2; i=i −1)

r e s = i ∗ r e s ;return r e s ;

Et bien sur en C et en Caml :

int f a c t ( const int x ) int i ;int r e s = 1 ;for ( i=x ; i >=2; i−−)

r e s = i ∗ r e s ;return r e s ;

let f a c t n =let nbr = r e f 1 infor i = 1 to n do

nbr := ( ! nbr ∗ i )

Page 74: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

74 CHAPITRE 5. RECURSIVITE, CALCULABILITE ET COMPLEXITE

done ;! nbr ; ;

Comment faire cette transformation, de facon automatique ?C’est faisable simplement dans le cas de la recursion terminale. En voici un

exemple (toujours pour la factorielle) :

stat ic int a f a c t ( int n , int acc ) i f (n == 0) return acc ;return a f a c t (n−1,n∗ acc ) ;

stat ic int t e rm ina l f a c t ( int n) return a f a c t (n , 1 ) ;

La propriete caracteristique de la recursion terminale est que dans la suited’appels effectues il n’y a pas besoin de memoriser ce qu’il reste a faire apres lesretours de fonctions (grace ici a l’accumulateur acc)

Cela permet au compilateur de transformer automatiquement en code ite-ratif, et donc de ne pas avoir a sauvegarder tous les etats intermediaires sur lapile. Dans ce cas il n’y a evidemment pas d”’explosion” memoire possible avecces appels recursifs.

La suite d’appel pour notre code de factorielle, version recursion terminaleest ainsi :

fact(3)

afact(3,1)

afact(2,3)

afact(1,6)

afact(0,6)=6

fact(3) renvoie donc 6 immediatement, sans aucun besoin de pile. Il y ajuste le cout d’un scalaire en plus : acc.

5.3 Recursivite et principe de recurrence struc-turelle

Les codes recursifs sont tres naturels quand on manipule...les structures dedonnees recursives. C’est le cas en particulier des fonctions que l’on peut definir

Page 75: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

5.3. RECURRENCE STRUCTURELLE 75

par recurrence structurelle. Donnons-en un exemple sur les listes lineaires, vuesau chapitre 4.

Soit P une propriete qui nous interesse sur un domaine de valeurs, parexemple pour commencer, les entiers naturels. Le principe de recurrence estle suivant :

– Si P est vraie en 0 (on ecrit P (0))– Et si P (n)→ P (n+ 1),

alors P est vraie sur tout N .Sur les listes lineaires d’entiers, on a de meme un principe de recurrence

structurelle :– Si P est vraie en () (la liste vide)– Et si P (l) vraie pour toutes les listes de longueur n implique P (cons(car, l))

est vraie (pour toute valeur de car, et toute liste l de longueur n) ;alors P est vraie sur tout ListN.

Ce principe de recurrence structurelle est la consequence directe de la recur-rence sur les entiers car on a la fonction totale length : ListN → N , qui verifielength(cons(hd, l)) = length(l) + 1.

Appliquons maintenant un principe de definition de fonction par recurrencestructurelle pour definir la fonction length, qui calcule la longueur d’une listelineaire d’entiers.

En Java, on ecrirait naturellement :

class L i s t . . .stat ic int l ength ( L i s t l )

i f ( l == null )return 0 ;

elsereturn l ength ( l . t l )+1;

Expliquons pourquoi ce code implemente bien le calcul de la longueur d’uneliste lineaire. Soit len(l), pour l une liste lineaire, la longueur definie mathema-tiquement, par recurrence structurelle :

– len(()) = 0– len(cons(car, l)) = len(l) + 1Soit maintenant P le predicat : P (l) = ”length(l) = len(l)”. Alors par

recurrence structurelle on prouve que P est vraie sur tout le domaine des listeslineaires :

– P (()) est vraie car length(()) = 0 = len(())– Supposons P (l) vraie pour toute liste de longueur n, on calcule

length(cons(car, l)) = length(l) + 1 = n+ 1

et len(cons(car, l)) = len(l)+1 = n+1 par hypothese de recurrence. Doncon a P (cons(car, l)).

Page 76: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

76 CHAPITRE 5. RECURSIVITE, CALCULABILITE ET COMPLEXITE

Ceci est un peu lourd pour faire la preuve d’une fonction si evidente, mais celapermet au moins de jouer un peu avec ce concept...

Remarque : ce principe de recurrence ne fonctionne que sur les listes lineaireset pas quelconques ; par exemple, la fonction length ne termine pas si on partde la liste circulaire l = cons(0, l).

Derniere remarque : c’est un cas de recurrence terminale, qui peut donc setransformer aisement en code iteratif :

stat ic int l ength ( L i s t l ) int i = 0 ;while ( l != null )

i = i +1;l = l . t l ;

return i ;

5.4 Partage en memoire et recursivite

Notre restriction sur les listes lineaires est un peu plus forte que necessaire,en fait, on peut partager des bouts de listes communs, si on s’assure que l’on necree pas de cycle... Dans ce cas, on peut toujours definir la fonction length etraisonner par recurrence structurelle. On economise juste en memoire, et celapeut permettre de faire du hash-consing sur les listes - c’est-a-dire permettre derepresenter l’egalite structurelle par l’egalite physique.

Donnons l’exemple, classique, de la fonction append : on veut concatenerune liste l2 au bout de la liste l1 :

stat ic L i s t append ( L i s t l1 , L i s t l 2 ) i f ( l 1 == null ) return l 2 ;i f ( l 2 == null ) return l 1 ;l 1 . t l = append ( l 1 . t l , l 2 ) ;return l 1 ;

En voici un exemple d’execution :

append ( l1 , l 3 ) ;append ( l2 , l 3 ) ;

Avec :

Page 77: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

5.4. PARTAGE EN MEMOIRE ET RECURSIVITE 77

l1 4 | . 5 | null

l2 1 | null

l3 2 | . 3 | null

A la deuxieme et derniere etape d’execution, on obtient :

l1 4 | . 5 | .

l2 1 | .

l3 2 | . 3 | null

En fait, on peut ecrire des codes pour append qui permettent a l’oppose, dene rien partager en memoire :

stat ic L i s t copy ( L i s t l ) i f ( l == null ) return null ;return new L i s t ( l . hd , copy ( l . t l ) ) ;

stat ic L i s t append ( L i s t l1 , L i s t l 2 ) i f ( l 1 == null ) return copy ( l 2 ) ; // re turn l 2 ;return new L i s t ( l 1 . hd , append ( l 1 . t l , l 2 ) ) ;

Que donne dans ce cas ? :

append ( l1 , l 3 ) ;append ( l2 , l 3 ) ;

avec les memes listes donnees en argument, que plus haut ?

Page 78: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

78 CHAPITRE 5. RECURSIVITE, CALCULABILITE ET COMPLEXITE

l3 2 | . 3 | null

append(l1, l3) 4 | . 5 | .

2 | . 3 | null

append(l2, l3) 1 | . 2 | .

3 | null

De facon plus generale, on peut ecrire ce code avec un partage possiblede l’argument gauche, de l’argument droit, des deux, ou d’aucun des deux.L’interet ou l’inconvenient, selon, des versions sans partage est que si on modifieen place les listes l1, l2 ou l3, append(l1,l3) ; et append(l2,l3) ; ne sontpas modifiees.

5.5 Les fonctions recursives primitives

Que calcule t-on dans le fragment purement imperatif (voir chapitre 2) sansla recursion, et en supposant que l’on n’a que comme type de donnees, lesentiers ? (et bien sur pas les piles !) On prouve assez facilement que l’on obtientla classe des fonctions recursives primitives (RP) qui est le plus petit ensemblede fonctions de Nn vers Nm contenant :

– les 3 fonctions de base : 0, succ (l’increment de 1), les projections– la composition de fonctions recursives primitives : si h, g1, . . . , gk sont des

fonctions RP, h(g1, . . . , gk) est dans RP– les fonctions definies par recursion primitive : g et h RP, g : Np → N,h : Np+2 → N, alors f : Np+1 → N definie par :– ∀y ∈ Np, f(0, y) = g(y)– ∀i ∈ N, y ∈ Np, f(succ(i), y) = h(i, f(i, y), y)

Les fonctions recursives primitives se programment dans tout langage deprogrammation imperatif pur, a l’aide d’une simple instruction iterative for :

f (x , y ) z = g (y ) ;for ( i =0; i<=x−1; i=i +1) z = h( i , z , y ) ;

return ( z ) ;

Page 79: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

5.5. LES FONCTIONS RECURSIVES PRIMITIVES 79

A l’inverse, malgre le peu de fonctions de base dans la definition des fonc-tions recursives primitives, on peut retrouver toutes les fonctions usuelles del’arithmetique sur les entiers. Par exemple, la fonction predecesseur est definiepar :pred (0 ) = 0pred ( succ (x ) ) = x

C’est exactement le schema de recursion primitive avec p = 0, g = 0, h(x, y) =x :

f(0) = g= 0

f(succ(i)) = h(i, f(i))= i

De meme, la somme de deux entiers est definissable comme suit :somme(0 , y ) = ysomme( succ (x ) , y ) = succ (somme(x , y ) )

C’est exactement le schema de recursion primitive avec p = 1, g(y) = y,h(x, z, y) = succ(z) :

f(0, y) = g(y)= y

f(succ(i), y) = h(i, f(i, y), y)= succ(f(i, y))

Le produit de deux entiers est a peine plus complexe :produi t (0 , y ) = 0produi t ( succ (x ) , y ) = somme(y , produi t (x , y ) )

C’est exactement le schema de recursion primitive avec p = 1, g = 0, h(x, z, y) =somme(y, z) :

f(0, y) = g= 0

f(succ(i), y) = h(i, f(i, y), y)= somme(f(i, y), y)

Tout ceci se generalise pour definir la somme de deux fonctions RP, le produitde deux fonctions RP etc.

Une consequence des axiomes definissant RP est le principe de minimisationbornee :

Si f : Np+1 → N est RP, et il existe M tel que quel que soit y ∈ Np, il existex ∈ N, x ≤M tel que f(x, y) = 0 ; alors :

g(y) = minx ∈ N, f(x, y) = 0

est une fonction RP de Np vers NOn peut alors definir les fonctions RP sont comme etant les fonctions conte-

nant 0, succ et les projections, stables par composition, et par minimisationbornee.

Page 80: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

80 CHAPITRE 5. RECURSIVITE, CALCULABILITE ET COMPLEXITE

5.6 Fonctions recursives partielles

Dans RP, on ne peut definir que des fonctions totales (qui terminent tou-jours), et l’on sait bien que ce que l’on ecrit naturellement dans un langage deprogrammation peut ne pas toujours terminer, pour de bonnes raisons ; on doitdonc etendre la classe de fonctions calculables.

Posons nous egalement la question de savoir si la fonction d’Ackermann estdans RP. On peut essayez de la definir avec des boucles... :

int ack ( int n , int p) i f (p==0)

for ( i =0; i<=n−1; i=i +1) ??. . .

Vous verrez, vous n’y arriverez pas, on a une sorte de recurrence emberlificoteesur 2 indices !

On va prouver qu’en fait ackermann n’est pas dans RP, mais elle est calculableau sens commun du terme et en un certain sens technique que l’on verra en finde chapitre. On voit donc bien qu’il va falloir trouver une autre definition defonction calculable. Il faut pouvoir ecrire des fonctions et les appeler de maniererecursive comme pour la definition ackermann

Montrons tout de meme avant cela que ackermann n’est pas une fonctionrecursive primitive, formellement.

Tout d’abord, en calculant quelques valeurs de A(m,n) on s’apercoit quec’est une fonction qui croıt extremement vite :

m / n 0 1 2 30 1 2 3 41 2 3 4 52 3 5 7 93 5 13 29 614 13 65533 265533 A(3,265533)5 65533 A(4,65533) A(4,A(5,1)) ...6 A(5,1) A(5,A(5,1)) A(5,A(6, 1)) ...

On peut en fait prouver que Ack croit plus vite que toute fonction RP :∀f : Nk → N ∈ RP , ∃a tel que f(a1, . . . , ak) < A(a,max(a1, . . . , ak)).Le schema de preuve est le suivant, on prouve successivement que :– la fonction x→ 0 est majoree par A(0, x) = x+ 1– la fonction x→ succ(x) est majoree par A(1, x) = x+ 2– les projections πn

k (x1, . . . , xn) = xk sont majorees parA(0,max(x1, . . . , xn)) = max(x1, . . . , xn) + 1

Pour simplifier les notations, on ecrit x = (x1, . . . , xn) et maxx = max(x1, . . . , xn).On prouve maintenant que si g1, . . . , gm sont telles que gi(x) < A(rgi ,maxx),

et h tel que h(x) < A(rh,maxx) alors f = h(g1, . . . , gm) est telle que f(x) <A(rf ,maxx) avec rf = h+ 2 +maxrgj

Page 81: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

5.7. POUR ALLER PLUS LOIN 81

Enfin, soit f ∈ RP definie par recursion primitive (avec g et h). Supposonsg(x) < A(rg,maxx), h(x) < A(rh,maxx), alors une recurrence simple montreque

f(i, x) < A(rf ,max(i, x))

avec rf = 1 +max(rg, rh).RP est le plus petit ensemble contenant 0, succ, les projections, stable par

composition et recursion primitive, donc toute fonction dans RP peut-etre ma-joree par un A(a, .).

On termine par un argument appele argument diagonal de Cantor :On considere g(a) = A(a, a) Supposons g dans RP, alors ∃rg, ∀a, g(a) <

A(rg, a). Mais c’est impossible, il suffit pour cela de considerer g(rg) = A(rg, rg) !

Que peut-on donc calculer au final ? (avec la recursion)Il nous faut tout d’abord definir les fonctions partielles, car certains calculs

peuvent ne pas terminer. Une fonction partielle f de Nn vers Nm est une fonctiondefinie sur un sous-ensemble de Nn, allant vers Nm. On note alors f(x) = ⊥pour les x ∈ Nn tels que f n’est pas definie en x (recodage en une fonction dansNn ∪ ⊥)

On appelle maintenant R l’ensemble des fonctions recursives partielles leplus petit ensemble de fonctions partielles de Nn vers Nm contenant :

– 3 fonctions de base : 0, succ, projections– la composition de fonctions recursives partielles : si h, g1, . . . , gk sont des

fonctions R, h(g1, . . . , gk) est dans R– R est clos par minimisation (pas forcement bornee cette fois-ci) : si f :

Np+1 → N est R, alors

g(y) = minx ∈ N | f(x, y) = 0

est une fonction R de Np vers NEn particulier si pour un y, il n’existe aucun x tel que f(x, y) = 0, alors g(y)est non-definie, i.e. g(y) = ⊥.

On a alors RP ⊆ R bien evidemment. De fait, R est exactement l’ensembledes fonctions partielles obtenues par au plus une minimisation d’une fonctiondans RP Et la fonction d’Ackermann est dans R !

5.7 Pour aller plus loin

Les fonctions de R sont aussi appelees “fonctions calculables” La these deChurch concernant la calculabilite dit que “les seules fonctions calculables al-gorithmiquement, par une machine, sont les fonctions de R”. C’est une theseverifiee dans tous les modeles de calcul connus : λ-calcul, tout langage de pro-grammation actuel, machine de Turing, machine de Post etc. On dit qu’unemachine (ou langage) est Turing complet s’il permet de calculer toutes les fonc-tions calculables - Java, C, Caml sont Turing complets.

Page 82: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

82 CHAPITRE 5. RECURSIVITE, CALCULABILITE ET COMPLEXITE

5.8 Quelques elements en complexite

Commencons par un cas d’etude simple en complexite algorithmique, l’in-version de liste :

etant donne une liste :

l 4 | . 5 | . 6 | null

On souhaite retourner la liste contenant les memes elements, mais enumeresdans l’ordre inverse :

li 6 | . 5 | . 4 | null

Une implementation possible est la suivante :

stat ic L i s t add ( f ina l L i s t x , f ina l int y ) i f ( x == null ) return new L i s t (y , null ) ;return new L i s t ( x . hd , add (x . t l , y ) ) ;

stat ic L i s t r e v e r s e ( f ina l L i s t x ) i f ( x == null ) return null ;return add ( r e v e r s e ( x . t l ) , x . hd ) ;

Si la liste est vide, on retourne vide, sinon on appelle recursivement l’inver-sion de liste sur la queue de la liste, et l’on rajoute sa tete a la fin, grace a uneautre fonction recursive, add.

En voici un exemple d’execution sur la liste donnee au debut de cette section :Etape 1 : on appelle reverse de :

l 4 | . 5 | . 6 | null

stat ic L i s t r e v e r s e ( f ina l L i s t x ) . . .return add ( r e v e r s e ( x . t l ) , x . hd ) ;

Etape 2 : qui appelle reverse de :

l.tl 5 | . 6 | null

Et reprendra en faisant add(.,4).

stat ic L i s t r e v e r s e ( f ina l L i s t x ) . . .return add ( r e v e r s e ( x . t l ) , x . hd ) ;

Page 83: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

5.8. QUELQUES ELEMENTS EN COMPLEXITE 83

Etape 3 : puis qui appelle reverse de :

l.tl.tl 6 | null

Et reprendra en faisant add(.,5).

stat ic L i s t r e v e r s e ( f ina l L i s t x ) . . .return add ( r e v e r s e ( x . t l ) , x . hd ) ;

Etape 4 : qui appelle reverse sur :

rev(l.tl.tl.tl)

stat ic L i s t r e v e r s e ( f ina l L i s t x ) i f ( x == null ) return null ;. . .

qui renvoie null.A l’etape 4 : cela appelle add(null,6) ; :

stat ic L i s t add ( f ina l L i s t x , f ina l int y ) i f ( x == null ) return new L i s t (y , null ) ;. . .

et renvoie :

rev(l.tl.tl) 6 | null

Etape 5 : puis add(.,5) sur le resultat precedent :

rev(l.tl.tl) 6 | null

stat ic L i s t add ( f ina l L i s t x , f ina l int y ) . . .return new L i s t ( x . hd , add (x . t l , y ) ) ;

Etape 6 : qui appelle add(null,5) et reconstruit une cellule contenant 6 entete :

add(null, 5) 5 | null

Page 84: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

84 CHAPITRE 5. RECURSIVITE, CALCULABILITE ET COMPLEXITE

stat ic L i s t add ( f ina l L i s t x , f ina l int y ) i f ( x == null ) return new L i s t (y , null ) ;. . .

Etape 7 : En faisant new List(6,.) sur le resultat precedent (on obtientainsi rev(l.tl)) :

rev(l.tl) 6 | . 5 | null

stat ic L i s t add ( f ina l L i s t x , f ina l int y ) . . .return new L i s t ( x . hd , add (x . t l , y ) ) ;

stat ic L i s t r e v e r s e ( f ina l L i s t x ) . . .return add ( r e v e r s e ( x . t l ) , x . hd ) ;

Etape 8 : puis add(.,4) sur le resultat precedent :

rev(l.tl) 6 | . 5 | null

stat ic L i s t add ( f ina l L i s t x , f ina l int y ) . . .return new L i s t ( x . hd , add (x . t l , y ) ) ;

A l’etape 9, cela appelle new List(6,add(x.tl,4)) donc appelle add(.,4)sur :

x.tl 5 | null

stat ic L i s t add ( f ina l L i s t x , f ina l int y ) . . .return new L i s t ( x . hd , add (x . t l , y ) ) ;

Et enfin aux etapes 10, 11, cela appelle new List(5,add(null,4)) qui ren-voie donc successivement :

add(null, 4) 4 | null

new List(5, add(null, 4)) 5 | . 4 | null

Page 85: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

5.8. QUELQUES ELEMENTS EN COMPLEXITE 85

stat ic L i s t add ( f ina l L i s t x , f ina l int y ) i f ( x == null ) return new L i s t (y , null ) ;. . .

Et enfin le dernier appel new List(6,.) s’effectue sur la liste precedente :

rev(...) 6 | . 5 | . 4 | null

stat ic L i s t add ( f ina l L i s t x , f ina l int y ) . . .return new L i s t ( x . hd , add (x . t l , y ) ) ;

stat ic L i s t r e v e r s e ( f ina l L i s t x ) . . .return add ( r e v e r s e ( x . t l ) , x . hd ) ;

On va maintenant compter le nombre d’operations elementaires effectueespar add (dans un premier temps), en fonction de la taille de ses arguments.On suppose un cout unitaire pour chaque operation arithmetique, pour chaquetest, pour chaque allocation memoire etc. Cela n’est pas tout a fait vrai sur unemachine moderne, mais cela n’est pas completement faux quand on raisonne aordre de grandeur pres. Enfin, la taille d’une liste l (de scalaires) est compteecomme etant egale a length(l).

Par recurrence, on prouve que add a un cout proportionnel a la longueur dex (complexite lineaire).

Supposons que la taille de x soit de n. Le premier appel (dans reverse) deadd se fait sur une taille n− 1 : cout proportionnel a n− 1. Le deuxieme appelde add, se fait sur une taille n − 2 : cout proportionnel a n − 2 etc. Le dernierappel de add se fait sur une taille 1 : cout proportionnel a 1.

Donc le cout total est proportionnel a 1 + 2 + . . .+ n− 1 ∼ n2.

Que veut-on dire par “est de l’ordre de” (∼) ?On va utiliser les notations de Landau et de Hardy ; comme en analyse (ici,

pour f , g deux fonctions de N vers N) :– f = O(g) ssi ∃α,N , ∀n ∈ N, n ≥ N =⇒ f(n) ≤ αg(n)– f = o(g) ssi ∀ε, ∃N , ∀n ∈ N, n ≥ N =⇒ f(n) ≤ εg(n)– f ∼ g ssi limn→∞

f(n)g(n) = 1

En fait, ce qui est plus utile pour nous est l’ordre de grandeur :– f = Ω(g) ssi ∃α,N , ∀n ∈ N, n ≥ N =⇒ f(n) ≥ αg(n)– f = Θ(g) ssi ∃α, β,N , ∀n ∈ N, n ≥ N =⇒ βg(n) ≤ f(n) ≤ αg(n)Donc ici pour notre code reverse naif la complexite est en Θ(n2).De facon generale, la complexite d’un algorithme peut etre definie en moyenne,

ou dans le cas le pire. Etant donne un probleme de taille n, la complexite d’unalgorithme est f(n), le nombre d’operations elementaires impliquees dans son

Page 86: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

86 CHAPITRE 5. RECURSIVITE, CALCULABILITE ET COMPLEXITE

calcul. Pour la complexite en moyenne, il s’agit de la moyenne arithmetique desnombres d’operations pour une taille n. Pour la complexite dans le cas le pire, ils’agit du maximum du nombre d’operations pour une taille n (sur une machinesequentielle).

La complexite d’un probleme est definie de facon similaire. Par exemple,la complexite (dans le cas le pire, et en moyenne) de reverse (naif) est enΘ(n2). La complexite d’un probleme est la meilleure complexite d’un algorithmeresolvant ce probleme. En fait, la complexite de l’inversion de liste est de Θ(n),cf. algorithme suivant.

Definissons maintenant une inversion non naıve de liste. On va utiliser uneliste intermediaire (accumulateur) :

stat ic L i s t revappend ( f ina l L i s t x , f ina l L i s t y ) i f ( x == null ) return y ;return revappend (x . t l , new L i s t ( x . hd , y ) ) ;

stat ic L i s t r e v e r s e ( f ina l L i s t x ) return revappend (x , null ) ;

On peut le coder aussi de facon iterative : on parcourt alors la liste initialedans un sens, pour construire la liste resultat dans l’autre !

stat ic L i s t r e v e r s e ( L i s t x ) L i s t r e s = null ;while ( x != null )

r e s = new L i s t ( x . hd , r e s ) ;x = x . t l ;

return r e s ;

Donnons en ici un exemple d’execution.A l’etape 1, on appelle reverse (donc revappend(x,y)) avec :

x 4 | . 5 | . 6 | null

y : null

stat ic L i s t r e v e r s e ( f ina l L i s t x ) return revappend (x , null ) ;

qui appelle a l’etape 2, revappend(x.tl, new List(4,null)) ; donc lesnouveaux arguments x et y sont :

Page 87: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

5.8. QUELQUES ELEMENTS EN COMPLEXITE 87

x 5 | . 6 | null

y 4 | null

stat ic L i s t revappend ( f ina l L i s t x , f ina l L i s t y ) . . .return revappend (x . t l , new L i s t ( x . hd , y ) ) ;

Etape 3 : cela appelle revappend(x.tl, new List(5,y)) ; donc les nou-veaux arguments x et y sont :

x 6 | null

y 5 | . 4 | null

stat ic L i s t revappend ( f ina l L i s t x , f ina l L i s t y ) . . .return revappend (x . t l , new L i s t ( x . hd , y ) ) ;

L’etape finale appelle revappend(null, new List(6,y)) ; donc les nou-veaux arguments x et y sont :

x : null

y 6 | . 5 | . 4 | null

stat ic L i s t revappend ( f ina l L i s t x , f ina l L i s t y ) i f ( x == null ) return y ;. . .

Cela est bien plus rapide. Etant donnee une liste de taille n, on fait un appela revappend sur une liste de taille n plus une operation elementaire (creationd’une cellule de liste) ; qui fait un appel a revappend sur une liste de taille n−1et ainsi de suite ; et se termine par return y. En tout, on a de l’ordre de Θ(n)operations, et on ne peut faire mieux sur une machine sequentielle.

On peut maintenant definir les classes de complexite algorithmiques.Donnons quelques exemples de complexite en temps. Dans l’ordre (stricte-

ment) croissant, les plus classiques sont :

Page 88: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

88 CHAPITRE 5. RECURSIVITE, CALCULABILITE ET COMPLEXITE

Temps Classe ExempleΘ(1) temps constant addition d’entiers (machine)Θ(log(n)) temps logarithmiqueΘ(n) temps lineaire inversion de listeΘ(n log(n)) temps quasi-lineaire triΘ(n2) temps quadratique produit matrice vecteurΘ(np) temps polynomial multiplication de matricesΘ(en) temps exponentiel plus tard...

On note classiquement : L pour le temps logarithmique (fonctions tres peucouteuses), P (ou PTIME) pour le temps polynomial (pas trop couteuses), etEXPTIME pour le temps exponentiel (tres couteuses).

Donnons maintenant quelques exemple d’ordres de grandeur (en secondes) :Classe/n 1 2 5 10 . . . 20 50

L 0... 0.3 0.7 1 1.3 1.7Θ(n) 1 2 5 10 20 50

Θ(n log(n)) 0... 0.6 3.5 10 26 85Θ(n2) 1 4 25 100 400 2500Θ(n3) 1 8 125 1000 8000 125000Θ(en) 2.7 7.4 148 22026 4.9.108 5.2.1021

Ce dernier nombre fait environ 1.6.1014 annees alors que l’age de l’universestime est d’environ 15.109 annees... notre systeme solaire aura disparu depuisbien longtemps...

Pour aller plus loin, disons un mot sur les classes NP et surtout la NP-completude. Sans rentrer dans les details, et sans definir precisement ce queveut dire NP, les problemes NP-complets sont les problemes les “plus durs”donton peut verifier la solution en temps polynomial. Helas beaucoup de problemesclassiques sont NP-complet. Par exemple SAT, qui consiste a determiner si uneformule logique (propositionnelle - cf. chapitre 7) est toujours vraie ou pas. Celaest, en termes de complexite, aussi complique qu’enumerer les valeurs booleennespour toutes les variables (exponentiel) et tester si la formule est vraie avec cesaffectations (polynomial) ! On ne connaıt a l’heure actuelle que des algorithmesEXPTIME au mieux pour resoudre les problemes NP-complets. On ne sait pasa l’heure actuelle si P 6= NP (mais bien sur P ⊆ NP ), et cela reste un problemea 1 million de dollars ! (Clay Institute)

Il y a egalement une notion de complexite en espace. On compte non pas letemps mais le nombre d’octets necessaire a resoudre un probleme algorithmique.On peut prouver alors la relation suivante entre la complexite en temps, et enespace :

L ⊆ P ⊆ PSPACE ⊆ EXPTIME

Pour aller plus loin, suivez le cours INF423 !

Page 89: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Chapitre 6

Semantique denotationnellede langages imperatifs

6.1 Semantique elementaire

On considere ici un langage jouet, sorte de fragment imperatif de Java, sansles tableaux ni les references. On appelle Var les variables, Val les valeurs quepeuvent prendre les variables ; on nomme egalement Val⊥ l’ensemble Val∪⊥,qui permet de rajouter une valeur symbolique “bottom” (⊥) representant unevaleur indefinie. On note les environnements ρ ∈ Env⊥ qui sont des fonctionspartielles de Var vers Val⊥ : ρ : Var → Val⊥. Par abus de notation, on definitl’environnement ⊥ tel que ⊥(x) = ⊥ pour tout x ∈ Var.

Ce langage permet de definir des expressions arithmetiques, que l’on noteAExpr, des tests, notes BExpr, et definit egalement des instructions : les affec-tations, les conditionnelles, et les boucles while.

La semantique pour les expressions arithmetiques est definie par recurrencestructurelle sur AExpr : pour a ∈ AExpr, on definit [[a]] : Env⊥ → Val⊥ par :

[[n]]ρ = n[[v]]ρ = ρ(v)[[a0 + a1]]ρ = [[a0]]ρ+ [[a1]]ρ[[a0 − a1]]ρ = [[a0]]ρ− [[a1]]ρ[[a0 ∗ a1]]ρ = ([[a0]]ρ) ([[a1]]ρ). . .

L’addition, la multiplication etc. sont en fait ici les extensions strictes des ope-rations correspondantes sur Val, c-.a-.d. que ⊥+x = x+⊥ = ⊥ pour l’addition,et ainsi de suite.

Pour b ∈ BExpr expression booleenne, on definit egalement par recurrence

89

Page 90: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

90 CHAPITRE 6. SEMANTIQUE DENOTATIONNELLE

structurelle [[b]] : Env⊥ → true, false⊥ par :

[[true]]ρ = true[[false]]ρ = false

[[a0 == a1]]ρ =

true si [[a0]]ρ = [[a1]]ρ 6= ⊥⊥ si [[a0]]ρ = ⊥ ou [[a1]]ρ = ⊥false sinon

[[a0 < a1]]ρ =

true si [[a0]]ρ < [[a1]]ρ⊥ si [[a0]]ρ = ⊥ ou [[a1]]ρ = ⊥false sinon

. . .

L’affectation est interpretee comme suit. Pour v ∈ Var ; e ∈ AExpr, etρ ∈ Env⊥ :

[[v = e]]ρ = ρ[[[e]]ρ/v]

ou pour u, v ∈ Var et n ∈ Val :

ρ[n/v](u) =ρ(u) si u 6= vn si u = v

Prenons un exemple simple de calcul de la semantique de l’affectation : Consi-derons x = 3 ∗ y + 5 dans l’environnement ρ tel que ρ(y) = 3 et ρ(x) = 1, onobtient l’environnement σ avec

σ(u) =ρ(u) = 3 si u = y[[3 ∗ y + 5]]ρ = 14 si u = x

Cette derniere egalite est vraie car dans l’environnement ρ ou ρ(y) = 3 :

[[3 ∗ y + 5]]ρ = [[3 ∗ y]]ρ+ [[5]]ρ= [[3 ∗ y]]ρ+ 5= ([[3]]ρ) ([[y]]ρ) + 5= 3 ∗ 3 + 5= 14

Pour la conditionnelle :

[[if b p1 else p2]]ρ =

[[p1]]ρ si [[b]]ρ = true[[p2]]ρ si [[b]]ρ = false⊥ si [[b]]ρ = ⊥

On a egalement la regle pour interpreter la sequence d’instruction :

[[p; q]]ρ = [[q]] ([[p]]ρ)

Faire en effet p puis q a partir de l’environnement ρ est equivalent a faire pa partir de l’environnement ρ, puis d’appliquer a ce nouvel environnement latransformation [[q]]. La sequence est transformee ainsi en une composition de

Page 91: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

6.2. PROBLEMES DE POINTS FIXES 91

fonctions. C’est pourquoi la semantique que l’on donne ici est appelee “compo-sitionnelle” (ou denotationnelle : a chaque element de syntaxe, on associe une“denotation”, de facon compositionnelle).

Traitons maintenant du cas de la boucle, qui est le point delicat de la se-mantique denotationnelle de notre langage. Observons tout d’abord que toutesles boucles ne terminent pas. Par exemple :

while ( true ) . . .

Donc [[P ]]ρ ne peut pas toujours renvoyer un σ ∈ Env, mais seulement unefonction partielle ou une fonction totale a valeur dans Env⊥, et on notera [[P ]]ρ =⊥ si P ne termine pas.

Essayons de determiner quelle devrait etre la semantique d’une boucle, apriori. Pour w = while b c , on doit avoir

w ∼ if b then c;w else skip

(skip est une instruction qui ne fait rien, [[skip]]ρ = ρ) Donc on doit avoir :

[[w]]ρ =

[[c;w]]ρ si [[b]]ρ = true[[skip]]ρ si [[b]]ρ = false

=

[[w]] ([[c]]ρ) si [[b]]ρ = trueρ si [[b]]ρ = false

C’est une definition circulaire, comment en donner un sens ? En fait, cela peuts’ecrire comme une equation aux points fixes. Soit φ une fonction partielle de Envvers Env - ou de facon equivalente une fonction totale de Env∪⊥ vers Env⊥,et stricte (φ(⊥) = ⊥). Soit F la fonctionnelle stricte envoyant φ : Env⊥ → Env⊥vers une autre fonction du meme type F (φ) : Env⊥ → Env⊥ :

F (φ)(ρ) =

φ ([[c]]ρ) si [[b]]ρ = trueρ si [[b]]ρ = false⊥ si [[b]]ρ = ⊥

On veut φ tel que φ = F (φ) point fixe (en fait le plus petit en un certain sens...).

6.2 Problemes de points fixes

Quel est le sens de ce point fixe maintenant ? On n’a helas pas de conti-nuite et de support compact, de fonctions k-lipschiptziennes et autres, qui nouspermettraient d’appliquer un des theoremes de point fixe de l’analyse mathema-tique. Mais on a une structure partiellement ordonnee de fonctions partielles,et des theoremes de points fixes pour ces structures, Tarski et Kleene, que nousallons decrire tout de suite.

Le cadre general est celui des ordres partiels. Un ordre partiel est un couple(P,≤) d’un ensemble P et d’une relation binaire ≤⊆ P × P telle que ≤ est

Page 92: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

92 CHAPITRE 6. SEMANTIQUE DENOTATIONNELLE

reflexive : ∀p ∈ P, p ≤ p ; transitive : ∀p, q, r ∈ P, p ≤ q & q ≤ r =⇒ p ≤ r ; etanti-symetrique : ∀p, q ∈ P, p ≤ q & q ≤ r =⇒ p = q.

Par exemple (R,≤) est un ordre partiel. En fait, on a meme pour cet ordrepartiel, la propriete que x ≤ y ou y ≤ x pour tout x, y ∈ R. C’est ce que l’onappelle un ordre total. Un autre exemple est (℘(S),⊆), qui est l’ensemble dessous-ensembles d’un ensemble S, avec pour ordre partiel, l’inclusion ensembliste.

Dans les cas qui nous interessent, on peut definir un ordre sur Env⊥ commesuit. Pour ρ ∈ Env⊥, ρ′ ∈ Env⊥,

ρ ≤ ρ′

siρ′(x) = ρ(x) si ρ(x) 6= ⊥

(ρ restriction de ρ′ a un sous-domaine de Var ; ou ρ′ extension de ρ).Dans un ordre partiel, il y a une notion de majorant, et de minorant. Soit

(P,≤) un ordre partiel. Pour X ⊆ P : p est un majorant de X si ∀q ∈ X, q ≤ p ;p est le plus petit majorant (sup) si : p est un majorant de X et pour tous lesmajorants q de X, p ≤ q. Le plus petit majorant, s’il existe, est note :

⋃X.

De meme (en renversant l’ordre), on definit les minorants, et le plus grand desminorants (inf) s’il existe, et que l’on note

⋂X. Un treillis est un ordre partiel

ayant un sup et un inf pour tout X a deux elements (et par recurrence, pourtout ensemble fini non vide X)

Reprenons nos exemples. (R,≤) est non seulement un ordre total, mais enplus, un treillis (le sup de deux elements est leur max, l’inf est leur min). Dememe, (℘(S),⊆) est un treillis : le sup de deux elements (ensembles) est leurunion ensembliste, l’inf de deux elements (ensembles) est leur intersection en-sembliste.

Donnons egalement des exemples d’ordres partiels qui ne sont pas des treillis.(Env⊥,≤) n’est pas un treillis : il suffit de considerer par exemple ρ et σ unique-ment definis sur x ∈ Var, avec ρ(x) = 1, σ(x) = 2. Que pourraient alors valoirρ ∪ σ ? On voit bien qu’on ne peut le definir.

Malgre tout, ce dernier ordre partiel a d’autres bonnes proprietes, de CPO,comme nous allons le definir dans la suite. Pour P ordre partiel, une chaıne deP est p0 ≤ p1 ≤ . . . ≤ pn. De meme, une ω-chaıne de P est p0 ≤ p1 ≤ . . . ≤pn ≤ . . ., c’est-a-dire est une suite infinie d’elements de P .

On dit qu’un ordre partiel (P,≤) est un CPO (Complete Partial Order) sitoute ω-chaıne (pi)i∈N de P admet un sup pour ≤, et si (P,≤) admet un pluspetit element encore note ⊥, c’est-a-dire ∀p ∈ P,⊥ ≤ p.

On dit egalement qu’un treillis est complet si tous ses sous-ensembles Xadmettent un sup et un inf (condition en fait redondante) ; en particulier, iladmet toujours un plus petit element : ⊥ =

⋃∅, et un plus grand element :

> =⋃P

Ainsi, (R,≤) n’est pas un treillis complet, ni un CPO, mais R∪−∞,∞ estun treillis complet (donc un CPO). De meme, (℘(S),⊆) est un treillis complet,avec ⊥ = ∅, > = S. (Env⊥,≤) est un CPO tel que pour toute ω-chaıne ρ0 ≤

Page 93: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

6.2. PROBLEMES DE POINTS FIXES 93

ρ1 ≤ . . . ≤ ρn ≤ . . . on a(⋃i∈N

ρi

)(x) =

ρj(x) si ∃j ∈ N, ρj(x) 6= ⊥⊥ sinon

A partir d’un CPO et d’un ensemble, on peut aisement construire un autreCPO, comme l’indique le lemme suivant :

Lemme 1 Supposons que C est un CPO, A est un ensemble. Alors CA (noteaussi A → C) l’ensemble des fonctions de A vers C, muni de l’ordre : f ≤g si ∀a ∈ A, f(a) ≤C g(a) est un CPO.

Preuve. Soit f0 ≤ f1 ≤ . . . ≤ une ω-chaıne dans CA, on note f∞ : A → Cla fonction definie par : pour tout a ∈ A, f∞(a) =

⋃i∈N fi(a) (raisonnable, car

pour tout a ∈ A, f0(a) ≤ f1(a) ≤ . . . est une ω-chaıne dans le CPO C).Alors f∞ ≥ fi pour tout i et si on suppose que l’on a g : A → C ≥ fi pour

tout i, on en deduit :

pour tout a ∈ A, g(a) ≥ fi(a), donc g(a) ≥⋃i∈N

fi(a) = f∞(a)

Certaines fonctions vont jouer un role particulier entre des CPOs : les fonc-tions continues, et les fonctions croissantes.

On dit qu’une fonction F : D → E d’un CPO (D,v) vers un CPO (E,⊆)est croissante si ∀d, d′ ∈ D, d v d′ ⇒ F (d) ⊆ F (d′).

Une fonction F croissante est dite continue si pour toutes les ω-chaınes d0 vd1 v . . . v dn v . . . de D, on a :

⋃n∈N

F (dn) = F

(⊔n∈N

dn

)

L’appellation de continuite vient de l’analogie avec la topologie, que l’on peutrendre precise au moins partiellement ici. Tout d’abord, remarquons que l’en-semble des ouverts O(X) d’un espace topologique X, muni de l’inclusion, formeun CPO. Maintenant, une fonction continue, au sens topologique du terme f :X → Y induit une fonction f : O(Y ) → O(X) par f(oY ) = f−1(oY ) ∈ O(X).f est ainsi croissante, et continue, au sens des structures ordonnees. Il existe enfait des correspondances exactes entre structures ordonnees et topologies (sou-vent non Hausdorff). C’est un sujet qui se trouve au coeur de la dualite de Stoneet de la theorie des domaines (fondement de la semantique denotationnelle delangages fonctionnels), que les etudiants interesses pourront poursuivre en M2.

Dans le cas de Env⊥ → Env⊥, on peut se poser la question de caracteriserles fonctions croissantes.

Soit f : Env⊥ → Env⊥ croissante. On obtient que si ρ′ est une extension deρ, f(ρ′) est une extension de f(ρ).

Page 94: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

94 CHAPITRE 6. SEMANTIQUE DENOTATIONNELLE

De meme, qu’est-ce qu’une fonction f : Env⊥ → Env⊥ continue ? C’est dejaune fonction f croissante et telle que pour toute ω-chaıne ρ0 ≤ ρ1 ≤ . . . ≤ ρn ≤. . . on a, pour tout x ∈ Var :(⋃

i∈N f(ρi))

(x) =f(ρj)(x) ∃j ∈ N, f(ρj)(x) 6= ⊥⊥ sinon

=

f(⋃

i∈N ρi

)(x) = f

(y →

ρj(y) ∃j ∈ N, ρj(y) 6= ⊥⊥ sinon

)(x)

Pour la semantique des boucles que l’on essaie de construire, le domained’interet est D = Env⊥ → Env⊥. L’ordre partiel sur ce domaine est definicomme suit. Pour φ ∈ D, ψ ∈ D, φ ≤ ψ, si pour tout ρ ∈ Env⊥, φ(ρ) ≤ ψ(ρ),c’est-a-dire si pour tout ρ ∈ Env⊥, φ(ρ) est une restriction de ψ(ρ) a un sous-domaine de Var C’est un CPO par le lemme 1.

Definissons maintenant ce que sont les points fixes, les pre-points fixes, etles post-points fixes.

Soit f : D → D croissante pour un ordre partiel D. Un point fixe de f estun element d de D tel que f(d) = d. Un post-point fixe de f est un element dde D tel que f(d) v d. Un pre-point fixe de f est un element d de D tel qued v f(d).

On a alors deux theoremes de point fixe tres classiques (on se servira surtoutdu deuxieme dans ce cours) :

Theoreme 1 (Tarski) Soit f : D → D une fonction croissante sur un treilliscomplet D. Alors f admet au moins un point fixe. De plus, l’ensemble des pointsfixes de f est un treillis complet, ainsi il existe toujours un unique plus petit pointfixe, note lfp(f) (“least fixed-point”) et un plus grand point fixe, note gfp(f)(“greatest fixed-point”).

Preuve. On considere m =⋂x ∈ D | f(x) ≤D x et M =

⋃x ∈ D | x ≤D

f(x). On montre que m est le plus petit point fixe de f et que M est le plusgrand point fixe de f .

Soit X = x ∈ D | f(x) ≤D x. Soit x ∈ X : on a m ≤D x, donc f(m) ≤D

f(x). Mais f(x) ≤D x parce-que x ∈ X. Donc f(m) ≤D x pour tout x ∈ X.Donc f(m) ≤D m.

Ainsi f(f(m)) ≤D f(m), ce qui implique que f(m) ∈ X, et doncm ≤D f(m).Enfin, on conclut : f(m) = m.

Dernier argument : m est defini comme etant l’inf d’un ensemble contenanten particulier tous les points fixes de f , donc m est non seulement un point fixemais le plus petit point fixe de f .

Theoreme 2 (Kleene) Soit f : D → D une fonction continue sur un CPO D(avec un plus petit element ⊥). Alors,

fix(f) =⊔n∈N

fn(⊥)

est le plus petit point fixe de f (qui existe ainsi !).

Page 95: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

6.3. SEMANTIQUE DE LA BOUCLE WHILE 95

Preuve. Par continuite de f :

f(fix(f)) = f(⊔

n∈N fn(⊥)

)=

⊔n∈N f

n+1(⊥)=

⊔n∈N f

n(⊥)= fix(f)

Supposons que d est un point fixe de f . On a ⊥ ≤D d, donc f(⊥) ≤D f(d) =d par croissance de f , et, par recurrence, fn(⊥) ≤D d. Ainsi fix(f) ≤D d.

6.3 Semantique de la boucle while

Revenons a l’interpretation des boucles while.Par recurrence sur les termes c du langage, on suppose [[c]] ∈ D, alors pour

φ ∈ D :

F (φ)(ρ) =

φ ([[c]]ρ) si [[b]]ρ = trueρ si [[b]]ρ = false⊥ si [[b]]ρ = ⊥

Pour pouvoir appliquer le theoreme 2 il faut prouver que F : D → D estcontinue, pour l’ordre sur D.

On commence par en prouver la croissance deja. Pour φ ≤D ψ, on verifieque F (φ) ≤D F (ψ) ; pour tout ρ ∈ Env⊥, par exemple dans le cas [[b]]ρ = true :

F (φ)(ρ) = φ([[c]]ρ)≤ ψ([[c]]ρ)= F (ψ)(ρ)

Maintenant, pour toute suite φ0 ≤D . . ., et tout ρ ∈ Env⊥ :⋃i

F (φi)(ρ) = F (⋃i

φi)(ρ)

Mais :

F (⋃

i φi)(ρ) =

i Φi([[c]]ρ) si [[b]]ρ = trueρ si [[b]]ρ = false⊥ si [[b]]ρ = ⊥

Le seul cas ou il y ait a prouver quelque chose est le premier ([[b]]ρ = true) :trivial, car (

⋃i Φi)σ =

⋃i(Φiσ) par definition de l’ordre (point a point).

Ceci donne donc la semantique de la boucle while. Montrons en pratique ceque cela veut dire. En fait, le theoreme de Kleene applique au probleme de lasemantique du while est exactement une semantique par approximations finies,ou on approxime en deroulant la boucle un peu plus a chaque fois.

Page 96: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

96 CHAPITRE 6. SEMANTIQUE DENOTATIONNELLE

On considere par exemple dans la suite, la semantique [[while (x<10) x=x+1]].On identifie ρ ∈ Env⊥ avec ρ(x) ∈ Val⊥ car il y a une seule variable. La fonc-tionnelle F est, pour Φ : Env⊥ → Env⊥ :

F (Φ)(ρ) =

Φ(ρ+ 1) si ρ < 10ρ si ρ ≥ 10⊥ si ρ = ⊥

A la premiere iteration, on trouve :

F 1(ρ) = F (⊥)(ρ) =

⊥ si ρ < 10ρ si ρ ≥ 10⊥ si ρ = ⊥

C’est ce que l’on obtient si on ne passe pas par le corps de boucle (on sort en 0iteration).

Puis a la deuxieme :

F 2(ρ) = F (F 1)(ρ) =

F 1(ρ+ 1) si ρ < 10ρ si ρ ≥ 10⊥ si ρ = ⊥

=

⊥ si ρ < 910 si ρ = 9ρ si ρ ≥ 10⊥ si ρ = ⊥

C’est ce que l’on obtient si on passe zero ou une fois par le corps de boucle (onsort en 0 ou 1 iteration).

Par recurrence, on prouve que l’on trouve, a la (n+ 1)ieme iteration :

Fn+1(ρ) = F (Fn)(ρ) =

⊥ si ρ < 10− n10 si ρ = 10− n, . . . , 9ρ si ρ ≥ 10⊥ si ρ = ⊥

C’est ce que l’on obtient si on passe de zero a n fois dans le corps de boucle. Lalimite

⋃n∈N F

n(⊥) contient tous les comportements possibles.

6.4 Semantique des fonctions recursives

Ce procede nous permet egalement de donner une semantique pour des fonc-tions mutuellement recursives. On suppose que l’on autorise dans notre langageles definitions recursives de ce type par exemple :

f1 = c11; f i11 ; c12; f i12 ; . . . ; f i1j1

f2 = c21; f i21 ; c22; f i22 ; . . . ; f i2j2

. . .

fk = ck1 ; f ik1 ; ck2 ; f ik

2 ; . . . ; f ikjk

Page 97: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

6.5. CONTINUITE ET CALCULABILITE 97

ou les cij sont des suites d’instructions, les f i sont des fonctions definies mu-tuellement, qui agissent sur le meme environnement (uniquement des variablesglobales ici).

On peut reprendre comme exemple le calcul de la fonction d’Ackerman :l’entree est la variable p, le calcul de ackermann(n, p) se fait par ackn(p), leresultat dans l’environnement est dans la variable q :

ack0 = (q = p+ 1)ack1 = if (p==0) p = 1; ack0

elsep = p− 1; ack1; p = q; ack0ack2 = if (p==0) p = 1; ack1

elsep = p− 1; ack2; p = q; ack1. . .

De facon plus generale, on considere k programmes “a trous”

P1(f1, . . . , fk), . . . , Pk(f1, . . . , fk)

ecrits dans notre langage et on cherche [[f1]], . . . , [[fk]] : Env⊥ → Env⊥ tels que(comme la semantique est compositionnelle) :

[[f1]] = [[P1(f1, . . . , fk)]] = [[P1]]([[f1]], . . . , [[fk]]). . .[[fk]] = [[Pk(f1, . . . , fk)]] = [[Pk]]([[f1]], . . . , [[fk]])

Ce qui produit une fonctionnelle (continue !)

F : Dk → Dk

pour laquelle on applique le theoreme de Kleene !On retrouve bien sur comme cas particulier la definition recursive d’une

boucle while :

while b c ∼ f = if b then c ; f else skip

6.5 Continuite et calculabilite

Le fait de pouvoir interpreter des schemas recursifs partiels generaux permetd’imaginer le paradigme suivant : notre semantique ne donne que des fonctions/-fonctionnelles continues, ce que l’on va prouver un peu plus bas. De facon plusgenerale, le credo est, les fonctions calculables sont les fonctions continues.

Rappelons que pour une fonction f : Env⊥ → Env⊥, etre continue veut direetre croissante et pour toute ω-chaıne ρ0 ≤ ρ1 ≤ . . . ≤ ρn ≤ . . . on a, pour toutx ∈ Var :(⋃

i∈N f(ρi))

(x) =f(ρj)(x) ∃j ∈ N, f(ρj)(x) 6= ⊥⊥ sinon

=

f(⋃

i∈N ρi

)(x) = f

(y →

ρj(y) ∃j ∈ N, ρj(y) 6= ⊥⊥ sinon

)(x)

Page 98: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

98 CHAPITRE 6. SEMANTIQUE DENOTATIONNELLE

La continuite est donc dans ce cas exactement le fait que l’on peut calcu-ler par limite d’approximations finies ; ceci est directement lie aux schemas dedefinition recursifs.

Prouvons dans un premier temps la croissance de l’interpretation des expres-sions. Les expressions arithmetiques a ∈ AExpr ou booleennes b ∈ BExpr :

[[a]] : Env⊥ → Val⊥[[b]] : Env⊥ → true, false⊥

sont croissantes, par recurrence structurelle sur les expressions... : si σ est uneextension de ρ, [[a]]ρ est definie (6= ⊥) implique [[a]]σ definie ; de meme pour [[b]].En fait, on a aisement un peu plus, que [[a]] et [[b]] sont continues.

Venons en maintenant a la croissance de l’interpretation de l’affectation. Soitφ ≤ ψ ∈ Env⊥, alors [[v = e]]φ = φ[[[e]]σ/v], ou, pour u, v ∈ Var et n ∈ Val :

φ[n/v](u) =φ(u) si u 6= vn si u = v

Et [[v = e]]ψ = ψ[[[e]]σ/v] ou, pour u, v ∈ Var et n ∈ Val :

ψ[n/v](u) =ψ(u) si u 6= vn si u = v

Necessairement : φ[n/v] restriction de ψ[n/v] si φ restriction de ψ. En fait, onprouve aisement que [[v = e]] est continue. C’est en effet a verifier pour chaquevariable u : en u = v consequence de la continuite de [[e]] et en u 6= v de par lacontinuite de l’identite.

Examinons maintenant la croissance de l’interpretation des tests. Ceci seprouve par recurrence sur les termes. Supposons que [[p1]], [[p2]] sont croissantes ;on considere le terme i ∼ if b p1 else p2 :

[[if b p1 else p2]]ρ =

[[p1]]ρ si [[b]]ρ = true[[p2]]ρ si [[b]]ρ = false⊥ si [[b]]ρ = ⊥

Alors si ρ ≤ ρ′ :– [[b]]ρ definie egale a true implique [[b]]ρ′ definie egale a true– dans ce cas [[i]]ρ′ = [[p1]]ρ′ et comme par recurrence [[p1]] est croissante,

[[p1]]ρ′ est une extension de [[p1]]ρ = [[i]]ρ– de meme quand [[b]]ρ est definie egale a false.

De meme, ceci est continu. C’est en fait evident, il suffit de separer le cas [[b]]ρ =true du cas [[b]]ρ = false.

Enfin, pour ce qui est de l’interpretation des boucles, on procede par recur-rence sur les termes c du langage. On suppose [[c]] ∈ D0 (continue), alors pourφ ∈ D0 (continue) :

F (φ)(ρ) =

φ ([[c]]ρ) si [[b]]ρ = trueρ si [[b]]ρ = false⊥ si [[b]]ρ = ⊥

est continue (car la composee de fonctions continues est continue...).

Page 99: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Chapitre 7

Logique, modeles et preuve

On commence par definir dans ce chapitre quelques concepts elementairesen logique des predicats du premier ordre. Cette logique nous sera utile pourdefinir et prouver des proprietes de programmes, puis au chapitre 9, elle nouspermettra de presenter l’isomorphisme de Curry-Howard.

La logique a ete creee dans un effort de formalisation des mathematiques,si l’on ignore comme ici sa partie philosophique. En particulier, les axiomes del’arithmetique de Peano, la theorie des ensembles de Zermelo-Fraenkel, sont destheories exprimables dans la logique des predicats du premier ordre.

7.1 Syntaxe de la logique des predicats du pre-mier ordre

Celle-ci est definie syntaxiquement comme suit :– Elle comprend des operateurs binaires (infixe) ∧ (et), ∨ (ou), ⇒ (implica-

tion), ⇔ (equivalence), unaire ¬ (negation), 0-aire (constantes) 1 (vrai),0 (faux) et un ensemble de variables infini

– Les quantificateurs : ∀ (pour tout), ∃ (il existe), des predicats de base,d’arite variable, P (x), Q(x, y), x ≤ y, x = y etc. et des fonctions d’aritevariable egalement, f(x), g(x, y), x2, x− y etc.

On appelle termes de la logique des predicats les elements de syntaxe formesa partir des variables, et inductivement, par application repetee de fonctions.Autrement dit, une variable x est un terme, et si t1, . . . , tn sont des termes, etf une fonction n-aire, alors f(t1, . . . , tn) est un terme. On appelle formule enlogique des predicats les elements de syntaxe definis comme suit :

– P (t1, . . . , tn) est une formule quand P est un predicat n-aire, et t1, . . . , tnsont des termes

– ¬Φ est une formule quand Φ est une formule– Φ ∧ Ψ, Φ ∨ Ψ, Φ ⇒ Ψ, Φ ⇔ Ψ sont des formules quand Φ et Ψ sont des

formules– ∀x.Φ et ∃x.Φ sont des formules quand Φ est une formule

99

Page 100: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

100 CHAPITRE 7. LOGIQUE, MODELES ET PREUVE

Dans une formule, on dit qu’une variable est libre quand elle n’est pas quan-tifiee. A contrario, une variable est liee quand elle est quantifiee. Par exemple,dans la formule ∀x.P (x, y, z) x est liee, y et z sont libres.

7.2 Semantique de la logique des predicats dupremier ordre

On peut definir une interpretation des termes, inductivement, exactementcomme en semantique denotationnelle de langages de programmation, chapitre6.

On se donne pour ce faire un modele, ou structure du premier ordre D (unensemble de “valeurs” ou de “denotations”).

A chaque symbole f d’arite n, on associe [[f ]] : Dn → D (par convention,pour les constantes, d’arite 0, [[f ]] ∈ D).

De meme, a chaque predicat P d’arite n on associe une fonction caracteris-tique χP : Dn → 0, 1. L’idee est que l’ensemble des valeurs de Dn, dans cetteinterpretation, telles que P est vraie, est χ−1

p (1).Etant donnes un modele D et une interpretation [[.]], on doit aussi interpreter

les variables x qui prennent des valeurs dans D, il nous faut donc une notiond’environnement, comme pour la semantique des langages de programmation.Un environnement est ici une fonction ρ : Var→ D.

L’evaluation des termes de la logique des predicats se fait sans surprise :

[[x]]ρ = ρ(x)[[f(t1, . . . , tn)]]ρ = [[f ]]([[t1]]ρ, . . . , [[tn]]ρ)

Pour les formules F de la logique des predicats, [[F ]]ρ va avoir une valeurdans 0, 1 :

[[Φ ∧Ψ]]ρ = ([[Φ]]ρ) ([[Ψ]]ρ)[[¬Φ]]ρ = 1− [[Φ]]ρ

On n’a pas besoin d’en dire plus, grace aux lois de Morgan (A∨B = ¬((¬A) ∧(¬B)), A ⇒ B = (¬A) ∨ B etc.), valides en logique propositionnelle (fragmentde la logique des predicats du premier ordre).

L’evaluation des formules se fait comme suit.– [[P (t1, . . . , tn)]]ρ = χP ([[t1]]ρ, . . . , [[tn]]ρ)– Quantificateurs :

[[∀x.Φ]]ρ =

1 si ∀ρ′ ∈ Env tq ρ′(y) = ρ(y) ∀y 6= x ∈ Var, [[Φ]]ρ′ = 10 sinon

[[∃x.Φ]]ρ =

1 si ∃ρ′ ∈ Env tq ρ′(y) = ρ(y) ∀y 6= x ∈ Var, [[Φ]]ρ′ = 10 sinon

Remarquez que l’egalite joue toujours un role particulier parmi les predicats,son interpretation n’est pas “libre”. Si elle fait partie des predicats d’une theorie,

Page 101: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

7.2. SEMANTIQUE 101

alors elle est toujours interpretee par l’egalite dans D :

[[t1 = t2]]ρ =

1 si [[t1]]ρ = [[t2]]ρ0 sinon

Une notion tres importante est celle de la satisfiabilite d’une formule delogique des predicats. Soit M une interpretation (domaine D, fonction seman-tique [[.]], environnement ρ) et Φ une formule, alors on dit que M satisfait Φ,ou M |= Φ si [[Φ]]ρ = 1. Cela n’a a vrai dire reellement de sens que pour lesformules Φ closes (c’est-a-dire toutes ses variables sont liees), meme si on peutdefinir une relation de satisfiabilite generale, qui ne nous servira pas ici. Onappelle tautologie, une formule qui est vraie dans toutes les interpretations.

Une theorie (du premier ordre) est un ensemble d’axiomes, c’est-a-dire deformules du premier ordre avec une certaine signature, que l’on suppose etrevraie.

Par exemple, la theorie des groupes peut etre axiomatisee en logique despredicats en supposant la signature suivante. Celle-ci inclut des fonctions : ∗(l’operation de groupe), −1 (l’inversion) et 1 (l’unite du groupe). Elle inclutaussi un seul predicat : = (egalite). Les axiomes de la theorie des groupes, c’est-a-dire les formules definissants les groupes, “au premier ordre” sont :

∀x.x ∗ 1 = x∀x.1 ∗ x = x∀x.x ∗ x−1 = 1∀x.x−1 ∗ x = 1∀x, y, z.x ∗ (y ∗ z) = (x ∗ y) ∗ z

La logique est dite du premier ordre car les quantificateurs ne s’appliquentqu’a des variables (simples), on ne peut par exemple, en logique du premierordre, quantifier sur des ensembles dans lesquelles les variables pourraient evo-luer. Plutot qu’a specifier des structures mathematiques, la logique des predicatsva surtout nous servir par la suite pour formaliser les proprietes de programme(validation, chapitre 8).

En general on suppose que l’ensemble d’axiomes est fini ou recursivementenumerable (en fait, cela a une consequence profonde en theorie des modeles,sinon a priori, cela semble etre un prerequis raisonnable).

Les modeles, ou les semantiques - car il peut y en avoir de nombreuses, de-pendant de l’ensemble sous-jacent D - et les theories entretiennent des rapportscomplexes. A tout modele (ex. R), on peut associer, etant donne une signa-ture (ex. pour R, =,×,+,−, /, 0, 1...), sa theorie du premier ordre, c-.a-.d.l’ensemble de toutes les formules avec cette signature, que satisfait le modele.Inversement, a toute theorie, on peut associer l’ensemble des modeles qui satis-font a cette theorie. Il n’y a pas neanmoins generalement de relation bijectiveentre modeles et theories, en logique du premier ordre. Une theorie un tant soitpeu interessante ne suffit generalement pas a decrire de facon unique le modeleque l’on voulait axiomatiser. Par exemple, la theorie des nombres reels au pre-mier ordre, quelle que soit la signature raisonnable choisie pour les predicats

Page 102: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

102 CHAPITRE 7. LOGIQUE, MODELES ET PREUVE

definit des modeles parfois bien differents des nombres reels construits par cou-pure de Dedekind. Ils sont appeles les reels non standards, et ont parfois uninteret pour faire du calcul infinitesimal (analyse non-standard). Il existe memedes modeles denombrables de la theorie des ensembles de Zermelo-Fraenkel !

Pour ceux qui voudraient en savoir plus, tout ceci est developpe en INF423(en particulier les theoremes de compacite et de Lowenheim-Skolem en theoriedes modeles).

7.3 Decidabilite des formules logiques

Revenons brievement aux proprietes de calculabilite definies au chapitre 5.On va voir que certains predicats, sur les entiers - on se restreint donc ici aD = N - sont calculables en un certain sens, c’est-a-dire que l’on peut determinersi ils sont satisfiables ou non, algorithmiquement, alors que d’autres, pas. Danscette derniere categorie, on va trouver des proprietes particulierement utiles ala validation de programmes, helas, voir le chapitre 8.

Soit F une formule de la logique des predicats du premier ordre. On choisitcomme domaine d’interpretation D = N. On dit que F est decidable si χF estdans R (recursive partielle).

Construisons maintenant un predicat particulier, sur les programmes Java,que l’on voit comme des entiers naturels, grace a un codage, que l’on ne donnepas precisement, mais dont l’existence est evidente (l’ensemble des programmesJava est denombrable, car les programmes sont finis, ecrits sur un alphabet fini).

On peut donc coder tout programme Java J en un entier naturel que l’onnote [J ], que l’on peut meme calculer de facon tres algorithmique. On consideremaintenant le predicat sur N P (n) =”le programme de numero n termine”.

Ce predicat est indecidable. C’est-a-dire qu’il n’existe pas d’algorithme quietant donne un programme, reponde en temps fini si ce programme termine oupas.

Donnons-en ici quelques elements de preuve. Elle procede par l’absurde :supposons qu’il existe un algorithme A qui prenne en argument un programmeJ de numero x prenant en argument un entier, et un entier n et renvoyant truesi J(n) termine, false sinon. Considerons maintenant le programme K suivant :

K(x) i f A(x , x )

while ( t rue )

Quelle est alors la valeur de K([K]) ? De deux choses l’une : si K termine sur[K] alors A([K],[K]) est vrai, donc K([K]) fait tant que (true) et netermine pas, contradiction. Ou alors, si K ne termine pas sur [K] alors A([K])est faux donc K([K]) termine, contradiction encore une fois !

Remarque : il s’agit d’un argument dit de la “diagonale de Cantor”, ou plussimplement, argument diagonal. Le probleme que l’on vient de considerer s’ap-pelle le “probleme de l’arret”.

Page 103: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

7.4. POUR ALLER PLUS LOIN... 103

7.4 Pour aller plus loin...

Il existe un corpus tres imposant de resultats fondateurs en decidabilite etindecidabilite de theories. Par exemple, l’arithmetique de Peano (l’arithmetiqueque vous connaissez) est indecidable. Par contre, la theorie de Presburger, de-crivant une arithmetique de nombres naturels, plus faible, est decidable.

7.5 Un peu de theorie de la demonstration

La theorie de la demonstration est une branche des mathematiques et de lalogique qui se preoccupe de savoir non pas quand une formule est “vraie” (dansun modele par exemple, c’est le probleme de satisfiabilite traite auparavant) maisplutot si une formule, dans un systeme formel, est prouvable, et de construireune preuve.

Plusieurs manieres de formaliser les preuves (comme les preuves mathema-tiques que vous faites au quotidien, sauf que celles-ci sont dans un format re-lativement informel, en langage naturel, et ne sont donc pas automatisablesdirectement).

Le premier formalisme est celui de la deduction naturelle (Gentzen 1934),pour presenter des preuves en logique des predicats du 1er ordre.

On va ici se contenter de parler de theorie de la demonstration dans le cadrede la logique classique du premier ordre, c’est-a-dire de la logique proposition-nelle quantifiee, qui est la logique des predicats du premier ordre, mais ou l’onn’a que des variables logiques, et aucun predicat...

Dans un premier temps, definissons la notion de regle d’inference R. Etantdonne les preuves des propositions p1, . . . , pn, on prouve q (en une etape) senote :

(R) :p1 p2 . . . pn

q

“Si on a une preuve de p1, de p2,. . ., de pn, alors on a une preuve de q en rajoutantl’inference R”. Un systeme formel en deduction naturelle est la donnee de reglesecrites dans ce format.

Ainsi, une preuve en deduction naturelle est un arbre construit a partir detelles regles, comme on va le montrer a partir d’un exemple un peu plus loin.

Voici donc le systeme de preuve presente sous format “deduction naturelle”de la logique propositionnelle quantifiee du 1er ordre.

Il y a tout d’abord les regles d’introduction, nommees ainsi car elle per-mettent, a partir de la preuve de sous-formule d’une formule p, d’en deduire lapreuve de p, construite a partir d’un connecteur logique (et, ou, implique etc.)et de ces sous-formules. On “introduit” donc en quelque sorte ces connecteurslogiques :

L’introduction pour le et :

(∧I)p q

p ∧ q

Page 104: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

104 CHAPITRE 7. LOGIQUE, MODELES ET PREUVE

Pour le ou, a gauche :(∨Ig)

p

p ∨ qPour le ou, a droite :

(∨Id)q

p ∨ qPour la quantification universelle :

(∀I)p

∀x.p

((∀I) valide seulement si x n’apparaıt dans aucune des hypotheses [non dechar-gees])

Pour l’implication :[p]...

(⇒ I) qp⇒q

La notion [p] demande une explication supplementaire : on dit que l’on de-charge l’hypothese p. Ceci se lit naıvement “si on a prouve p, et que a partir decette preuve on peut prouver q, alors on peut prouver p⇒ q”.

Pour la quantification existentielle :

(∃I)p[a/x]∃x.p

On trouve ensuite les regles d’elimination : ce sont les regles inverses enquelque sorte. Si on a une preuve d’une formule composee de sous-formules, onveut en deduire la preuve d’une de ces sous-formules :

Pour le et, a gauche, et a droite :

(∧Eg) p∧qp (∧Ed) p∧q

q

Pour l’implication :(⇒ E)

p p⇒ q

q

Pour le ou, c’est un peu subtil :

(∨E)

[p] [q]...

...p ∨ q r r

r

Ce qui veut dire que si, etant donne une preuve (que l’on n’a pas) de p, on peutprouver r, et une preuve (que l’on n’a pas) de q, on peut prouver r, alors si ona une preuve de p ∧ q, on a une preuve de r.

Page 105: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

7.5. UN PEU DE THEORIE DE LA DEMONSTRATION 105

Pour la quantification universelle :

(∀E)∀x.pp[a/x]

(a est n’importe quelle formule, et [a/x] denote comme toujours la substitutionde la variable x par a)

Pour la quantification existentielle :

(∃E)

[p]...

∃x.p qq

Enfin, il y a des regles specifiques liees a F (faux) :

(F )F

p(RPA)

[¬p]...Fp

(la derniere regle est la reduction par l’absurde)Voici maintenant un exemple de preuve en deduction naturelle. On prouve

ici que p∧q ⇒ q∧p (qui est bien une formule vraie en logique propositionnelle).L’arbre de preuve est ainsi construit :

(⇒ I)(∧I) (∧Ed) [p∧q]

q (∧Eg) [p∧q]p

q ∧ pp ∧ q ⇒ q ∧ p

Remarquez que les hypotheses sont dechargees par la regle d’introduction del’implication (⇒ I).

Un autre format, dont il est plus difficile a comprendre l’interet et la relativecomplexite par rapport a la deduction naturelle, est le calcul des sequents. Cecalcul a ete introduit par Gentzen en 1936, apres la deduction naturelle donc,pour obtenir une formulation plus symetrique des regles de preuve, et pour eviterla notion de preuve “dechargee”.

Le format des regles est comme en deduction naturelle :

premissesconclusion

Les premisses et conclusion sont constitues de jugements de preuves.

Γ ` ∆

ou Γ et ∆ sont des suites de formules logiques. En fait, sequent est une “mauvai-se” traduction de l’allemand, et aurait sans doute du etre traduit par “sequence”ou “suite”. Ce format de regle se lit informellement de la facon suivante : “en

Page 106: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

106 CHAPITRE 7. LOGIQUE, MODELES ET PREUVE

supposant toutes les formules de Γ prouvees, on peut prouver la disjonction detoutes les formules de ∆”.

On distingue traditionnellement plusieurs groupes de regles en calcul dessequents :

“Groupe identite” :On a la regle “axiome”; d’une preuve de A je peux construire une preuve de

A :(ax)

A ` ALa regle de coupure, fondamentale dans la relation de la theorie de la preuve

avec l’informatique (isomorphisme de Curry-Howard, chapitre 9) :

(cut)Γ ` A,∆ Γ′, A ` ∆′

Γ,Γ′ ` ∆,∆′

On peut d’une certaine facon eliminer le besoin d’une preuve de A pour prouverles sequents de ∆′ si on a par ailleurs une preuve de A.

Ensuite vient le “groupe structurel” :On a l”’affaiblissement a gauche” :

(weakg)Γ ` ∆

Γ, A ` ∆

C’est-a-dire que l’on peut rajouter une hypothese (A) et toujours a arriver aprouver ∆ a partir du sequent Γ, A, si on a pu le prouver a partir de Γ.

De meme on a l”’affaiblissement a droite” :

(weakd)Γ ` ∆

Γ ` A,∆

On peut aussi “contracter” les sequents, si on a plusieurs copies de preuves :

(contrg)Γ, A,A ` ∆

Γ, A ` ∆

Remarquez la symetrie (autour du symbole `) des regles :

(contrd)Γ ` A,A,∆

Γ ` A,∆

On a egalement les regles d’echange :

(exg)Γ ` ∆

σ(Γ) ` ∆

(exd)Γ ` ∆

Γ ` σ(∆)

ou σ est une permutation agissant sur les sequents (l’ordre dans lequel on a listeles preuves).

Page 107: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

7.5. UN PEU DE THEORIE DE LA DEMONSTRATION 107

Puis on trouve le “groupe logique” :On a une regle d’introduction a droite de l’implication :

(⇒ Id)Γ, A ` B,∆

Γ ` A⇒ B,∆

De meme, a gauche :

(⇒ Ig)Γ ` A,∆ Γ ` B,∆

Γ, A⇒ B ` ∆

On a l’introduction a gauche du et :

(∧Ig)Γ, A,B ` ∆

Γ, A ∧B ` ∆

Puis l’introduction a droite du et (toujours cette symetrie !) :

(∧Id)Γ ` A,∆ Γ ` B,∆

Γ ` A ∧B,∆

Et d’autres regles encore...pour les autres connecteurs logiques. L’objectifde ce cours n’est pas d’etre complet de ce cote, mais de donner le minimum deconcepts logiques pour comprendre le probleme de la validation de programmetraite au chapitre 8, et le lien entre preuve et execution (theorie du typage,isomorphisme de Curry-Howard), traite au chapitre 9. Normalement, ces sujetsfont l’objet d’un ou plusieurs cours de deuxieme annee de Master.

Voici maintenant un exemple de preuve en calcul des sequents, pour la memeformule que l’on avait prouvee en deduction naturelle, c’est-a-dire p∧ q ⇒ q∧p.On construit encore un arbre de preuve :

(∧Id)(∧Ig)

(exg)(wg)

(ax) q`q

q, p ` qp, q ` q

p ∧ q ` q

(∧Ig)(wg)

(ax) p`p

p, q ` pp ∧ q ` p

p ∧ q ` q ∧ p

Notre explication de la theorie de la preuve a ete assez informelle malgretout ; il nous faudrait pour etre complet, donner une “semantique” des reglesd’inferences introduites, que ce soit en deduction naturelle ou en calcul dessequents. Ceci n’est pas sans interet, meme sans rentrer dans les details, carcette semantique ressemble tout a fait a celle developpee pour les langages deprogrammation, au chapitre 6, tout comme la facon d’exprimer la satisfiabilited’une formule de la logique des predicats du premier ordre ressemblait a s’ymeprendre a la semantique (denotationnelle) d’un langage de programmation.

Donc sans rentrer trop dans les details, soit P l’ensemble des formules lo-giques que l’on peut ecrire dans notre logique propositionnelle quantifiee du1er ordre. Chaque regle d’inference R definit une fonction FR : P → P (de

Page 108: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

108 CHAPITRE 7. LOGIQUE, MODELES ET PREUVE

production de nouvelles formules vraies, et enleve les formules dechargees dansle cas de la deduction naturelle). L’ensemble des propositions prouvables parle systeme formel decrit en deduction naturelle/calcul des sequents est le pluspetit ensemble invariant par l’application des FR, R regle d’inference. Il s’agitdonc du calcul du plus petit point fixe d’une fonctionnelle sur les ensembles,comme pour la semantique de la boucle while (voir chapitre 6). Dit de faconplus simple, il s’agit du calcul d’une cloture transitive de l’application des regles(“arbres de preuve”), ce qui donne le calcul effectif de ce plus petit point fixepar le theoreme de Kleene.

Terminons ce chapitre en disant quelque mots du rapport entre la satisfiabi-lite et la preuve en logique propositionnelle quantifiee du premier ordre.

Notons ` p si p est prouvable en logique propositionnelle quantifiee (par lesysteme de deduction naturelle precedent par exemple), et M |= p si p est satis-fiable dans le modele M de la theorie de la logique propositionnelle quantifieedu 1er ordre et |= p si p est satisfiable dans tous les modeles M .

On a alors les faits suivants.Le premier s’appelle la “correction” du systeme de preuve : si ` p alors |= p.

Ceci est vrai ici (en fait, toujours, sinon c’est un grave probleme du systeme depreuve) : si p est prouvable en deduction naturelle ou calcul des sequents, alorsp est “vrai”, ce qui est plutot rassurant.

L’autre propriete potentiellement interessante est la completude : si |= palors ` p ; c’est-a-dire que si p est vraie, elle est prouvable dans notre systemeformel. La encore, cela est vrai, (prouve dans la these de Godel en 1929).

Mais d’une certaine facon c’est plutot rare, cela n’est deja plus vrai pourle calcul des predicats du premier ordre, des que l’on s’autorise des predicatsun peu utiles. Ceci est lie au 2eme probleme de Hilbert (“mecanisation” del’arithmetique) de 1900. On a par exemple l’incompletude de l’arithmetique dePeano en calcul des predicats du premier ordre (Godel, encore)...

Page 109: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Chapitre 8

Validation et preuve deprogrammes

8.1 La validation, pour quoi faire ?

Prenons un petit exemple de programme, du bon fonctionnement duquelnous voudrions nous assurer. Il s’agit d’un code de transformee de Fourier rapide(dont on n’a pas indique completement certains points, dont certaines valeursde constantes, mais cela n’est pas important pour la preuve que l’on vise) :f f t ( complex ar ray re f a , int n) complex ar ray re f b [ n /2 ] , c [ n / 2 ] ;

i f (n > 2) for ( i=0 ; i < n ; i=i +2) b [ i /2 ] = a [ i ] ;

c [ i /2 ] = a [ i +1] ; f f t (b , n /2 ) ;f f t ( c , n /2 ) ;for ( i=0 ; i < n ; i=i +1)a [ i ] = F1(n)∗b [ i ] + F2(n)∗ c [ i ] ;

else a [ 0 ] = g∗a [ 0 ] + d∗a [ 1 ] ;

a [ 1 ] = a [ 0 ] − 2∗d∗a [ 1 ] ;

On souhaiterait pouvoir prouver que ce programme ne comporte pas debug a l’execution (division par zero, acces a des tableaux en dehors de leursbornes, depassement de valeurs pour les types consideres etc.). On souhaiteraitegalement prouver des proprietes plus fines, plus “fonctionnelles”, par exempleverifier l’egalite de Parseval (a la precision finie pres) :∑

i

| a′[i] |2=∑

i

| a[i] |2

Pour ce faire, on souhaite entrelacer le code avec des commentaires qui de-crivent, en utilisant la logique des predicats du premier ordre, des proprietes

109

Page 110: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

110 CHAPITRE 8. VALIDATION ET PREUVE DE PROGRAMMES

que l’on arrive a prouver pour toutes les executions du programme, passant parcette ligne. On appelle cela des annotations de preuve.

Progressivement, on annote de la premiere ligne aux lignes suivantes :

1 f f t ( a , n)

2 // a.length=n ∧ ∃k > 0 n=2k

3 cp lx b [ n /2 ] , c [ n / 2 ] ;

4 // a.length=n ∧ ∃k > 0 n=2k ∧ b.length=n2∧ c.length=n

2

5 i f (n > 2)6 for ( i =0; i<n ; i=i +2)

7 // a.length=n ∧ ∃k > 0 n=2k ∧ b.length=n2∧ c.length=n

2∧ i≥0 ∧ i<n

∧ ∃j ≥ 0 i=2j8 b [ i /2]=a [ i ] ;9 // i+1<n ?

10 c [ i /2]=a [ i +1] ; 11 . . .

On arrive facilement a prouver l’annotation en ligne 9, c’est-a-dire qu’il n’ya pas d’acces en dehors du tableau. En effet, i + 1 = 2j + 1 ≤ n et n = 2k

impliquent i+ 1 < n.

Un peu plus loin dans le code :

f f t ( a , n )

// a.length=n ∧ ∃k > 0 n=2k

cp lx b [ n /2 ] , c [ n / 2 ] ;

// a.length=n ∧ ∃k > 0 n=2k ∧ b.length=n2∧ c.length=n

2

i f (n > 2) for ( i =0; i<n ; i=i +2)

// a.length=n ∧ ∃k > 0 n=2k ∧ b.length=n2∧ c.length=n

2∧ i≥0 ∧ i<n

∧ ∃j ≥ 0 i=2jb [ i /2]=a [ i ] ;// i+1<nc [ i /2]=a [ i +1] ;

f f t (b , n /2 ) ;f f t ( c , n /2 ) ;for ( i =0; i<n ; i=i +1)a [ i ]=F1(n)∗b [ i ]+F2(n)∗ c [ i ] ;

else // a.length=2

a [0 ]= g∗a [0 ]+d∗a [ 1 ] ;a [1 ]= a [0]−2∗d∗a [ 1 ] ;

On arrive encore a prouver que l’on termine bien l’appel recursif avec

a.length=2, car n = 2l, 0 ≤ l ≤ k et n ≤ 2 impliquent a.length = n = 2.

Comment formaliser et automatiser cela ?

Page 111: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

8.2. PREUVE A LA HOARE 111

8.2 Preuve a la Hoare

Definissons dans un premier temps un langage d’assertion. Les expressionsarithmetiqued a ∈ Aexprv sont de la forme :

a ::= n | X | i | a0 + a1 | a0 − a1 | a0 × a1

Pour les expressions booleennes Assn :

A ::= true | false | a0 = a1 | a0 ≤ a1 |A0 ∨A1 | A0 ∧A1 | ¬A | A0 =⇒ A1 | ∀i A | ∃i A

Dans cette grammaire, on a deux types de variables : les variables des pro-grammes que l’on va analyser (X∈ Var) et des variables supplementaires, utiliseesuniquement pour le raisonnement logique (i∈ IntV ar). Par exemple, pour lavalidation de la FFT a la section 8.1, on a utilise le fait que la taille du tableauest une puissance de 2, et pour ce faire on a du introduire une variable k, pourecrire n = 2k. Cette variable (k) n’est pas une variable du programme.

Une interpretation de ces formules est une fonction I : IntV ar → Z, ouIntV ar est l’ensemble des variables entieres specifiques aux assertions. La se-mantique des assertions depend de I et de l’environnement courant σ :

[[n]](I, σ) = n[[X]](I, σ) = σ(X)[[i]](I, σ) = I(i)[[a0 + a1]](I, σ) = [[a0]](I, σ) + [[a1]](I, σ)

On definit maintenant par induction structurelle le jugement de preuve σ |=I

A, voulant dire : “pour σ ∈ Σ, σ satisfait A dans l’interpretation I” :– σ |=I true– σ |=I (a0 = a1) si [[a0]](I, σ) = [[a1]](I, σ)– σ |=I A ∧B si σ |=I A et σ |=I B– σ |=I A⇒ B si (non σ |=I A) ou σ |=I B– σ |=I ∀i.A if σ |=I[n/i] A pour tout n ∈ Z– etc.On definit une relation de satisfaction entre les environnements et les as-

sertions de correction partielle (invariants), pour une interpretation I, commesuit :

σ |=I AcB ssi(σ |=I A⇒ [[c]]σ |=I B

)On ecrit |=I AcB si pour tout σ ∈ Σ, σ |=I AcB.Malgre tout, |=I n’est pas tout a fait ce que l’on voulait ; par exemple :

i < XX = X + 1i < X

depend de i et de l’interpretation de i, a laquelle on ne s’interesse pas du tout.Une solution est de definir le jugement |= AcB si pour toutes les inter-

pretations I, |=I AcB. On souhaite maintenant determiner A et B de faconsystematique, pour toutes les instructions c, telles que |= AcB.

Page 112: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

112 CHAPITRE 8. VALIDATION ET PREUVE DE PROGRAMMES

On ne veut pas utiliser la definition de la validite des formules pour les tripletsa la Hoare, reposant sur le test exhaustif de la validite des formules pour chaqueenvironnement (satisfiabilite en theorie des modeles). On veut avoir un calculd’inferences logiques permettant de prouver la validite d’un triplet de Hoare,sans reference au modele d’execution concret (theorie de la demonstration, voirla section 7.5).

On definit maintenant des regles de preuve de correction partielle. Les voici,une par une ; elles seront prouvees et expliquees un peu apres (avec un exemple) :

– (skip)AskipA

– (assign)B[a/X]X = aB

Cela veut dire que pour que B soit vrai apres l’affectation de a a la variableX, il faut que que B ou l’on a syntaxiquement substitue l’expression a atoutes les occurrences de X, soit vraie.

– (seq)Ac0C Cc1BAc0; c1B

– (if)A ∧ bc0B A ∧ ¬bc1BAif b then c0 else c1B

– (while)A ∧ bcA

Awhile b do cA ∧ ¬b

– (conseq.)|= (A⇒ A′) A′cB′ |= (B′ ⇒ B)

AcB

Le symbole |= est ici celui de la section 7.2 : il denote la satisfiabiliteen logique des predicats de la formule qui est a droite (etant donne despremisses, a gauche de ce symbole).

Il y a deux proprietes importantes a examiner sur un systeme formel commecelui-ci, en rapport avec le programme analyse.

La premiere propriete est la correction de ce systeme formel : chaque asser-tion de correction partielle derivee a partir du systeme formel precedent doitetre vraie, c’est-a-dire coherente avec la semantique denotationnelle du chapitre6. Autrement dit :

` ApB ⇒|= ApB

Dans la formule plus haut ` ApB veut dire que l’on peut prouver ApBa partir du systeme formel donne plus haut, au sens de la theorie de la demons-tration (section 7.5). Le symbole |= a droite dans cette meme formule denote lasatisfaction de la formule correspondante, definie, par semantique denotation-nelle, au debut de cette section.

Page 113: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

8.2. PREUVE A LA HOARE 113

La deuxieme propriete qui pourrait etre souhaitable est la completude :toutes les assertions de correction partielle vraies dans notre semantique de-notationnelle doivent etre derivables dans notre systeme formel de regles decorrection partielle, c’est-a-dire, formellement :

|= ApB ⇒` ApB

On a alors le theoreme suivant :

Theoreme 3 On a la correction, mais pas la completude.

Preuve. Correction de (assign). On rappelle que la regle est : (assign)B[a/X]X = aB. On veut donc prouver que quelque soient σ ∈ Env,I : IntV ar → Z,

σ |=I B[a/X]⇒ [[X = a]]σ |=I B

Cela se fait par induction structurelle sur B. Par exemple :– B = C ∧ D, alors σ |=I B[a/X] est equivalent a σ |=I C[a/X] et σ |=I

D[a/X]. Par recurrence on sait que

σ |=I C[a/X]⇒ [[X = a]]σ |=I Cσ |=I D[a/X]⇒ [[X = a]]σ |=I D

– B = (a0 = a1), par recurrence sur les expressions a0, a1. Juste un exemple :a0 = X, a1 = n (constante), alors B[a/X] = (a = n) et bien sur σ |=I

(a = n) implique [[a]]σ = n donc [[X = a]]σ(X) = [[a]]σ = n d’ou [[X =a]]σ |=I (X = n)

Correction de la sequence. On rappelle la regle : (seq) Ac0C Cc1BAc0;c1B . La

preuve est alors une consequence directe de [[c0; c1]]σ = [[c1]] ([[c0]])σ. En effet, onsuppose [[σ]] |=I A. Soit ρ = [[c0]]σ : par hypothese (seq), ρ |=I C. Par hypothese(seq) encore, [[c1]]ρ |=I B. Donc comme [[c0; c1]]σ = [[c1]] ([[c0]])σ = [[c1]]ρ on a leresultat voulu.

Correction de la conditionnelle. On rappelle : (if) A∧bc0B A∧¬bc1BAif b then c0 else c1B .

On suppose σ |=I A. De deux choses l’une (j’ignore le cas ⊥) :– [[b]]σ = true : alors σ |=I A∧ b. Donc par hypothese (if) et par recurrence,

[[c0]]σ |=I B. Or dans ce cas [[if b then c0 else c1]]σ = [[c0]]σ.– Ou : [[b]]σ = false : alors σ |=I A ∧ ¬b. Donc par hypothese (if) et par

recurrence, [[c1]]σ |=I B. Or dans ce cas [[if b then c0 else c1]]σ = [[c1]]σ.Correction de la boucle. On rappelle : (while) A∧bcA

Awhile b do cA∧¬b . On sup-pose σ |=I A, alors de deux choses l’une :

– [[b]]σ = true : alors σ |=I A ∧ b. Donc par hypothese (while) et par recur-rence, σ1 = [[c]]σ |=I A. Or [[w=while b do c]]σ = [[if b then c ;w]]σ qui estegal dans ce cas a [[w]] ([[c]]σ). On arrive donc a une situation ou on cherchea prouver [[w]]σ1 |=I A ∧ ¬B quand σ1 |=I A. Si le nombre d’iteration estinfini et [[w]]σ = ⊥ |=I false et false⇒ A∧¬b ! Sinon, par recurrence surle nombre d’iteration de la boucle si elle est finie, on arrive a σn |=I A et[[b]]σn = false (cas suivant).

Page 114: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

114 CHAPITRE 8. VALIDATION ET PREUVE DE PROGRAMMES

– Ou : [[b]]σ = false : alors σ |=I A∧¬b. Or dans ce cas [[while b do c]]σ = σ.Maintenant, montrons informellement l’incompletude des regles de preuve.

Le langage d’assertion Assn contient au moins l’arithmetique de Peano du pre-mier ordre. Le theoreme d’incompletude de Godel implique en particulier qu’ilexiste des assertions vraies pour un programme, qui ne peuvent etre prouveespar notre systeme de deduction formel.

De plus, notre schema de preuve n’est pas entierement automatisable. Lesoutils bases sur un systeme de preuve a la Hoare (Spec], Frama-C, ...) sont engeneral interactifs.

On considere le programme suivant, pour donner un exemple d’utilisationde notre systeme de preuve :

w ≡ (while X > 0 do Y = X ∗ Y ; X = X − 1; )

On veut prouver :

X = n ∧ n ≥ 0 ∧ Y = 1wY = n!

Il faut en premier lieu trouver un invariant de boucle suffisamment fort pourpouvoir prouver notre assertion. C’est bien sur la que reside la principale diffi-culte pour l’automatisation ! On pose :

I ≡ Y X! = n! ∧X ≥ 0

On doit donc prouver :

I ∧X > 0Y = X ∗ Y ;X = X − 1; I

Prouvons maintenant l’invariant de boucle. Par la regle d’affectation, on a :

I[(X − 1)/X]X = X − 1; I

ou I[(X − 1)/X] ≡ (Y (X − 1)! = n!) ∧ X − 1 ≥ 0. Par la regle d’affectationencore une fois :

XY (X − 1)! = n! ∧X − 1 ≥ 0Y = X ∗ Y ; I[(X − 1)/X]

Par la regle de composition sequentielle :

XY (X − 1)! = n! ∧ (X − 1) ≥ 0Y = X ∗ Y ;X = X − 1; I

On a bien sur :

I ∧X > 0 ⇒ Y X! = n! ∧X ≥ 0 ∧X > 0⇒ Y X! = n! ∧X ≥ 1⇒ XY (X − 1)! = n! ∧ (X − 1) ≥ 0

Donc, par la regle d’affaiblissement :

I ∧X > 0Y = X ∗ Y ;X = X − 1; I

Page 115: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

8.2. PREUVE A LA HOARE 115

On applique la regle pour les boucles while :

IwI ∧X 6> 0

Et on a (X = n) ∧ (n ≥ 0) ∧ (Y = 1)⇒ I :

I ∧X 6> 0 ⇒ Y X! = n! ∧X ≥ 0 ∧X 6> 0⇒ Y X! = n! ∧X = 0⇒ Y 0! = Y = n!

Alors, par la regle d’affaiblissement :

(X = n) ∧ (Y = 1)wY = n!

En general la preuve et le code sont imbriques, pour mieux presenter lapreuve, comme ce que l’on avait fait en debut de cette section :

X = n ∧ n ≥ 0 ∧ Y = 1while (X>0) I ∧X > 0

Y=X∗Y;I[(X − 1)/X]

X=X−1;IY = n!

Un dernier mot sur la decidabilite de ce systeme de preuve. Consideronspour un programme quelconque P le programme Q suivant (x est une variablen’apparaissant pas dans P) :

int x=0;. . .P. . .x=1;

On souhaite prouver le triplet de Hoare trueQx = 1. Ceci est equivalent auprobleme de l’arret (chapitre 7) qui est indecidable.

Ces methodes, ou en tout cas des systemes de preuve d’esprit similaire, onete implementees en “vrai”. Une application classique est ce que l’on appellela programmation par contrats, qui a une longue histoire depuis Hoare 1974 :ecriture des preconditions et postconditions pour chaque fonction (contrats) - enmeme temps que le code ; ajout d’invariants dans le code pour aider le prouveurpour une verification. Par exemple, c’est inclus dans le langage Eiffel (BertrandMeyer 1985). Ou plus recemment, dans les outils de developpement Microsoft :Code Contracts/Spec] pour .net/C] (2009).

Page 116: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

116 CHAPITRE 8. VALIDATION ET PREUVE DE PROGRAMMES

Page 117: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Chapitre 9

Typage, et programmationfonctionnelle

Dans ce chapitre, nous abordons certains aspects de la programmation fonc-tionnelle. Au lieu de se concentrer sur la syntaxe d’un langage en particulier(OCaml par exemple, que la plupart d’entre vous connaissent, mais pas tous),on va utiliser un langage un peu abstrait, qui comprend tous les ingredientsclassiques des langages fonctionnels. On aurait pu utiliser le λ-calcul, mais celaest abstrait de facon non necessaire pour ce cours introductif, on a plutot choisiPCF, qui est plus proche d’un vrai langage de programmation.

9.1 PCF (non type)

Dans les langages fonctionnels, la particularite est que les fonctions sont desobjets “de premiere classe”. Dans PCF en particulier, on aura la constructionde fonction fun x -> t correspondant au Caml (au nommage pres) :

let f x = t

L’application d’une fonction a un argument est notee, comme en OCaml, t t.Remarquez que on peut appliquer une fonction a une fonction, et meme a soi-meme, dans PCF sans typage. Ceci nous posera d’ailleurs quelques soucis pourdefinir une semantique comprehensible de PCF, on a choisi ici une semantiquedite “operationnelle” qui a le double avantage d’etre plus facile a developper ici(qu’une semantique denotationnelle classique), et qui vous permettra de voirune autre facon de donner une semantique a un langage de programmation, enquelque sorte plus proche de l’implementation en machine. Ceci sera developpea la section 9.2.

Finalement, la grammaire complete de PCF est la suivante :

117

Page 118: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

118 CHAPITRE 9. TYPAGE, ET PROGRAMMATION FONCTIONNELLE

t ::= x| fun x -> t| t t | t× t| n| t+ t | t− t | t ∗ t | t/t| ifz t then t else t| fix x t| let x = t in t

Remarques : On pourrait rajouter une construction somme, comme au chapi-tre 4, mais nous ne compliquerons pas inutilement la semantique ici. Remarquezegalement que le langage PCF est complet au sens de Turing. Il permet decalculer toutes les fonctions recursives partielles, cf. chapitre 5.

Nous avons deja rencontre toutes les constructions syntaxiques plus haut,a des differences mineures. Par exemple ifz est le test a zero, ce qui est unpeu different de ce que nous avons rencontre dans le langage imperatif jouet duchapitre 6.

Nous n’avons pour l’instant par rencontre fix. Il s’agit d’un operateur depoint fixe qui permet de definir des fonctions recursives (interdites syntaxique-ment dans PCF). Par exemple, la fonction factorielle sera definie en PCF par :

fix f fun n -> ifz n then 1 else n ∗ (f(n− 1))

C’est la “plus petite” fonction f telle que

f(n) =

1 si n = 0n ∗ f(n− 1) sinon

La semantique denotationnelle en avait ete donnee au chapitre 6 (plus petitpoint fixe d’une certaine fonctionnelle). En Caml, cela correspond au let rec.

9.2 Semantique operationnelle

On va decrire les actions, une a une, lors de l’execution d’un programmePCF (semantique petits pas). Cela va prendre la forme de regles de reductionou de reecriture :

p→ q

ou “le terme p se reecrit (ou se reduit en une etape) en le terme q”En fait, ces regles vont former un automate (cf. programme de taupe) a

partir d’un programme PCF.Les noeuds du graphe de transition, ou de l’automate, seront des termes PCF,

c’est-a-dire des programmes. Les actions de l’automate, ou les arcs du graphe detransition sont appeles des regles de reduction, car en quelque sorte ces actionsconsistent a modifier le programme, au fur et a mesure de son execution, jusqu’aobtenir une forme residuelle, ou plus rien n’est executable.

Page 119: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

9.2. SEMANTIQUE OPERATIONNELLE 119

La regle la plus importante est la β-reduction :

(fun x -> t)u→ t[u/x]

ou t[u/x] est le terme t dans lequel on remplace syntaxiquement toutes lesoccurrences de la variable x par le terme u. C’est celle qui explique commentsont evaluees les fonctions, a partir des arguments.

On a egalement des regles decrivant le calcul arithmetique, qui sont asseztautologiques :

p+ q → n

si l’entier p plus l’entier q est egal a n. On ne les donne pas pour la multiplicationni pour les autres operations, cela est bien sur similaire.

Pour les conditionnelles on a la regle :

ifz 0 then t else u→ tifz n then t else u→ u si n 6= 0

Pour l’operateur de point fixe :

fix x t→ t[fix x t/x]

On va un peu jouer avec cette regle par la suite, elle peut paraıtre un peumagique, mais cela doit vous rappeler les regles de calcul de point fixe que l’ona vues au chapitre 8 en preuve a la Hoare.

Enfin nous avons une regle pour la definition :

let x = t in u→ u[t/x]

Donnons un exemple simple : un petit calcul arithmetique.

(fun x -> x+ 2) 3 → 3 + 2 β-reduction→ 5 regles arithmetiques

Remarquez que l’on a souligne les parties des termes PCF qui interagissent,et qui vont etre reduites. Ces parties s’appellent des redex.

En fait, comme on l’avait annonce en introduction, ce langage est assezredondant. On pourrait se passer de l’arithmetique par exemple.

Definissons :

[n] = fun z -> fun s -> s(s(s(. . . (s z) . . .)))

(ou on repete n ∈ N fois l’application de s) En quelque sorte, ce terme re-presente l’entier n. On peut ensuite coder facilement les operations, addition,multiplication :

+ = fun n -> fun p -> fun z -> fun s -> ns(psx)× = fun n -> fun p -> fun z -> fun s -> n(pf)z

Page 120: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

120 CHAPITRE 9. TYPAGE, ET PROGRAMMATION FONCTIONNELLE

C’est un codage connu sous le nom de entiers de Church (du nom d’AlonzoChurch, 1930). On pourrait faire de meme pour les booleens et pour la condi-tionnelle.

Une question naturelle est la suivante : definissons-nous bien quelque choseavec ces regles de reduction ? Pour cela, il serait bon de pouvoir s’assurer de laterminaison du processus de reduction.

Il n’est helas pas vrai que le calcul de reduction termine toujours, en voiciun exemple :

fix x x→ fix x x→ . . .

donc ne termine pas. En meme temps, que veut-dire ce terme ? On va en reparlertout de suite, et aussi a la section 9.6.

Ces termes sont en effet pratiques, il permettent egalement de se passer duterme fix , en tout cas, tant que l’on est dans un cadre non type.

Definissons le combinateur Y , qui permet en quelque sorte de remplacer leterme fix :

fun f -> (fun x -> f (x x))(fun x -> f (x x))

Soit alors g un terme PCF, on a :

Y g = (fun f -> (fun x -> f (x x))(fun x -> (f (x x))))g

β-reduction externe= (fun x -> g (x x))(fun x -> g (x x))

β-reduction interne= g(fun x -> g (x x))(fun x -> g (x x))= g (Y g)

Oui mais, on aurait pu aussi evaluer de la facon suivante ce terme, eneffectuant la deuxieme β-reduction (interne) avant la premiere. On aurait alorseu :

Y g = (fun f -> (fun x -> (f (x x))(fun x -> (f (x x))))

)g

(β-reduction interne)= (fun f -> (f (fun x -> f (x x)))

(fun x -> f (x x))) g(β-reduction interne)

= (fun f -> f f (fun x -> f (x x))(fun x -> f (x x))) g

= etc. !

Et cela ne termine pas encore une fois (en fait cela devrait vraiment vous rappelerl’iteration de Kleene...). C’est bien sur le meme phenomene que l’on avait avecle terme fix x x !.

Page 121: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

9.3. ORDRES D’EVALUATION 121

9.3 Ordres d’evaluation

On voit qu’en fait, on n’a pas specifie l’ordre d’utilisation des regles de re-duction, et que l’on peut voir pour le meme terme, des reductions qui terminentet d’autres qui ne terminent pas. Par contre, quand on choisit des reductionsqui terminent, est-ce que le resultat depend de la facon dont on reduit ?

Appelons terme irreductible, dans PCF, un terme sur lequel on ne peutappliquer aucune regle de reduction. On a une propriete de confluence : si onutilise les regles de reduction dans n’importe quel ordre qui termine sur unterme irreductible, alors on termine toujours sur le meme terme irreductible.Ceci est clairement faux si on oublie la condition d’irreductibilite, penser encorea fix x x.

Donnons un exemple :

(fun f -> fun x -> f(f x))(fun x -> x+ 2)

fun x -> (fun x -> x+ 2)(fun x -> x+ 2)x

fun x -> (fun x -> x+ 2) (x+ 2)fun x -> (fun x -> (x+ 2) + 2) x

fun x -> (x+ 2) + 2

On voit bien qu’en general, beaucoup d’ordres d’evaluation sont possibles,on va voir maintenant que parmi ceux-ci, un certain nombre ont une signifi-cation. On va voir que certains ordres d’evaluation correspondent au passaged’argument par valeur (comme pour les langages imperatifs dont nous avionsdonne la semantique au chapitre 6), ou au passage d’argument par reference, ouencore par necessite (semantique du langage fonctionnel Haskell).

9.4 Appel par nom, appel par valeur et appelpar necessite

Commencons ici par imposer un ordre d’evaluation. Ici, on va reduire lessous-termes les plus profonds (sans etre trop formel). Cela revient a evaluerd’abord les arguments des fonctions, avant les fonctions elles-memes. C’est lepassage d’arguments par valeur.

Page 122: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

122 CHAPITRE 9. TYPAGE, ET PROGRAMMATION FONCTIONNELLE

Exemple :

(fun x -> (x+ x))(2 + 3) → (fun x -> (x+ x)) 5evaluation de l’argument

→ 5 + 5→ 10

terme irreductible !

Evaluons maintenant les redexs de l’exterieur vers l’interieur. Cela revient acalculer ce que l’on peut d’une fonction, sans les arguments, et de n’evaluer lesarguments qu’au besoin, petit a petit. Il s’agit d’un passage par reference desarguments : on ne regarde ce qui est pointe par une reference, qu’au besoin.

En voici un exemple : Y g, en tout cas, l’evaluation qu’on en avait faite audebut de ce chapitre, et qui termine. On ne veut pas evaluer en effet a l’interieurde Y , pour avoir la terminaison.

Une question naturelle est alors de savoir si l’on peut quand meme definirun operateur de point fixe pour l’appel par valeur. La reponse est positive, ils’agit du combinateur Z (dans un langage non type, encore une fois) :

fun f -> (fun x -> f (fun v -> ((x x)v)))(fun x -> f (fun v -> ((x x)v)))

Donnons un autre exemple, ici d’ appel par nom :

(fun f -> fun x -> f(f x))(fun x -> x+ 2)

fun x -> (fun x -> x+ 2)(fun x -> x+ 2) x

fun x -> (fun x -> x+ 2) (x+ 2)fun x -> (fun x -> (x+ 2) + 2) x

fun x -> (x+ 2) + 2

On rappelle que l’on avait choisi pour l’appel par valeur, l’evaluation sui-vante :

Page 123: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

9.5. COMBINATEURS DE POINT FIXE 123

(fun f -> fun x -> f(f x))(fun x -> x+ 2)

fun x -> (fun x -> x+ 2)(fun x -> x+ 2) x

fun x -> (fun x -> x+ 2) (x+ 2)fun x -> (fun x -> (x+ 2) + 2) x

fun x -> (x+ 2) + 2

En fait, il existe une autre evaluation des termes PCF, qui s’appelle l’appelpar necessite, et qui est une variante de l’appel par nom avec partage des sous-termes et des reductions correspondantes. C’est donc assez proche de l’appelpar nom, et permet aussi de definir simplement des combinateurs de points fixetype Y. Il est implemente dans le langage fonctionnel Haskell (pas Caml), quiest un langage cree en 1987 et nomme en l’honneur du logicien Haskell Curry.Il est certes plus dur a compiler efficacement, mais parfois plus souple pour leprogrammeur. On en donne un exemple tout de suite avec l’implementation de“structures de donnees infinies”.

Par exemple, en Haskell, on peut definir les choses suivantes :

numsFrom n = n : numsFrom (n+1)squares = map (\ˆ2) ( numsfrom 0)take 5 squares => [ 0 , 1 , 4 , 9 , 1 6 ]

numsFrom n construit une liste infinie d’entiers, commencant en n. squaresapplique la fonction “carree” sur la liste infinie (0, 1, 2, . . .). take extrait unprefixe fini : c’est l’evaluation par necessite de ce terme qui demande juste cequ’il faut d’evaluation de la liste infinie numsFrom 0.

9.5 Combinateurs de point fixe, en Haskell, Camlet Java

En Haskell egalement, on peut programmer directement (bien que non ne-cessaire) un combinateur de point fixe (mais pas le code de Y ), qui va terminer :

y ( f ) = f (y f )f a c t f n = i f (n == 0) then 1 else n ∗ f (n−1)y ( f a c t ) 10. . .

Page 124: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

124 CHAPITRE 9. TYPAGE, ET PROGRAMMATION FONCTIONNELLE

Remarquez les types :

f : α→ αy : (α→ α)→ (α→ α)

fact : (int→ int)→ int→ int

Alors qu’en Caml, a priori, on ne peut pas coder le combinateur Y a causede l’appel par valeur (et du typage, cf. plus loin dans ce chapitre), mais on peuttricher un peu : on peut utiliser une cloture :

let rec f i x f x = f ( f i x f ) x

let f a c t ab s f a c t = function0 −> 1| x −> x ∗ f a c t (x−1)

let x = ( f i x f a c t ab s ) 5

Remarquez les types :

va l f i x : ( ( ’ a −> ’ b ) −> ’ a −> ’ b ) −> ’ a −> ’ b = <fun>va l f a c t ab s : ( i n t −> i n t ) −> i n t −> i n t = <fun>va l x : i n t = 120

On peut s’en sortir aussi avec des references, bien sur, et des types recursifs :

type ’ a r e c c = In of ( ’ a r e c c −> ’ a )let out ( In x ) = x

let y f = ( fun x a −> f ( out x x ) a )( In ( fun x a −> f ( out x x ) a ) )

Peut-on coder cela en Java ? Il faut etre serieusement fou, mais on va utiliserune forme faible des clotures, en les codant par des objets JAVA (et des interfaces- cf. chapitre 4).

Commencons par definir :

class YFact // i n t −> i n tinterface IntFunc int apply ( int n ) ;

// ( i n t −> i n t ) −> ( i n t −> i n t )interface IntFuncToIntFunc

IntFunc apply ( IntFunc f ) ; ;

// Higher−order func t i on re tu rn ing an i n t f unc t i on// F: F −> ( i n t −> i n t )interface FuncToIntFunc

IntFunc apply ( FuncToIntFunc x ) ;

// Function from IntFuntToIntFunc to IntFunc// (( i n t −> i n t ) −> ( i n t −> i n t ) ) −> ( i n t −> i n t )interface IntFuncToIntFuncToIntFunc

IntFunc apply ( IntFuncToIntFunc r ) ; ;

Page 125: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

9.6. TYPAGE 125

Maintenant, le code JAVA de Z et de factorielle est le suivant :

(new IntFuncToIntFuncToIntFunc ( ) public IntFunc apply ( f ina l IntFuncToIntFunc r )

return (new FuncToIntFunc ( ) public IntFunc apply ( f ina l FuncToIntFunc f )

return f . apply ( f ) ; ). apply (new FuncToIntFunc ( ) public IntFunc apply ( f ina l FuncToIntFunc f )

return r . apply (new IntFunc ( ) public int apply ( int x )

return f . apply ( f ) . apply (x ) ; ) ; ) ;

Dans ce code, new correspond a une construction de fonction fun, et applycorrespond a une application dans PCF (apply(p).q=p q).

On peut verifier que ce code (du a Ken Schiriff) fonctionne :

public stat ic void main ( St r ing args [ ] ) System . out . p r i n t l n (// Z combinator. . .. apply (// Recurs ive func t i on genera tornew IntFuncToIntFunc ( )

public IntFunc apply ( f ina l IntFunc f ) return new IntFunc ( )

public int apply ( int n) i f (n == 0) return 1 ;else return n ∗ f . apply (n−1); ;

) . apply (// Argument

I n t eg e r . pa r s e In t ( args [ 0 ] ) ) ) ;

En l’executant :

> javac YFact . java> java YFact 103628800

9.6 Typage

Jusqu’a present, nous avons considere un langage fonctionnel non type.Qu’est-ce que le typage ? L’interet du typage est d’eliminer des termes qui pa-raissent n’avoir aucun sens, en fait, comme on le verra plus tard, ils font partied’une preuve de bon fonctionnement, au sens theorie de la preuve.

Donc un bon systeme de typage doit par exemple eliminer des choses comme(fun x -> x) + 1. Et finalement, il sera difficile de ne pas eliminer non plus destermes tels fun x -> x x ni Y , car il est difficile de concevoir x comme a lafois une fonction, et un argument a lui-meme. La premiere consequence de cette

Page 126: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

126 CHAPITRE 9. TYPAGE, ET PROGRAMMATION FONCTIONNELLE

remarque est donc qu’un langage type aura a priori un combinateur de pointfixe explicite.

On peut faire plusieurs choix concernant le typage, dans les langages de pro-grammation : on peut avoir un langage ou on declare les types et ou il y a uneverification minimale des types (Java etc.), eventuellement avec regles de trans-typage (Java, C etc.). Il s’agit de ce que l’on appelle un typage faible. Ou alors, onpeut faire le choix d’un langage avec inference de types (Caml etc.), et ou ceux-ci (hors references...) assurent un bon comportement des programmes, minimal,mais de l’ordre de la preuve (a la Hoare, ou presque) de certaines proprietes deprogramme. Il s’agit alors d’un typage fort. En quelque sorte, le typage fortest une preuve de coherence du programme, tres formelle. On verra brievementcette idee a travers une illustration de la correspondance type et formule delogique / programme de ce type et preuve de cette formule (isomorphisme deCurry-Howard), a la fin de ce chapitre.

On peut demarrer par associer des types relativement simples a des termesPCF, appeles ici types monomorphes :

τ ::= int| τ → τ| τ × τ

Les types sont donc ici, soit entier (int - soit a vrai dire des types de basespre-specifies), soit un type fonctionnel (→), soit encore un type produit (×).

Le defaut de tels types est que, par exemple, la fonction identite n’aurapas de “type plus general”. La fonction identite, appliquee a un entier, a letype int → int. Appliquee a une fonction de type int → int, elle aura le type(int→ int)→ (int→ int) et ainsi de suite.

Une facon de remedier a ce defaut, est d’introduire du “polymorphisme” (onen a vu une forme dans les langages orientes objets au chapitre 4) :

τ ::= int | bool | . . . types de base| τ × τ type produit| τ → τ type d’une fonction| α variable de type| ∀α.τ type polymorphe

On a ainsi rajoute des “variables de type”, qui permettent de gagner enaisance. Ainsi, la fonction identite aura comme type α → α, ou α pourra etreinstancie plus tard a n’importe quel autre type. C’est ce que fait le typage deOCaml par exemple.

Donnons maintenant une semantique au typage des termes PCF. Pour typerune expression, on a besoin de la connaissance du typage de l’environnementEnv. Au lieu d’avoir Env = Var → Val, un environnement Γ associe a chaquevariable x, un type Γ(x) dans notre grammaire de types. On ecrira souventΓ, x : τ pour l’environnement qui vaut Γ (defini sur toutes les variables sauf x),et dans lequel x a le type τ .

Page 127: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

9.6. TYPAGE 127

Dans l’environnement Γ, l’expression e (de PCF) a le type τ se note :

Γ |= e : τ

C’est ce que l’on appelle un jugement de typage On va definir un systeme formelcomme au chapitre 8, permettant de deriver ces jugements de typage. La deri-vation du terme d’un terme PCF donnera lieu a un arbre de typage, exactementcomme pour les systemes de preuve du chapitre 5.

Les regles de typage sont ainsi :Pour les variables :

Γ |= x : Γ(x)

Pour les constantes :

Γ |= n : int

Pour les operations arithmetiques :

Γ |= s : int Γ |= t : intΓ |= s+ t : int

et ainsi de suite, pour les autres operations arithmetiques, de facon evidente.Pour la creation de fonctions :

Γ, x : A |= t : BΓ |= fun x -> t : A→ B

Pour l’application :

Γ |= u : A Γ |= v : A→ B

Γ |= v u : B

Pour l’affectation :

Γ |= t : A Γ, x : A |= u : BΓ |= let x = t in u : B

Pour la conditionnelle :

Γ |= t : int Γ |= u : A Γ |= v : AΓ |= ifz t then u else v : A

Pour l’operateur de point fixe :

Γ, x : A |= t : AΓ |= fix x t : A

Et enfin pour la paire :

Γ |= u : A Γ |= v : BΓ |= (u, v) : A×B

Page 128: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

128 CHAPITRE 9. TYPAGE, ET PROGRAMMATION FONCTIONNELLE

Donnons un exemple de typage. Considerons le terme : let f = fun x -> x+1 in f 2. On a alors l’arbre de jugements de typage :

...x : int |= 1 : int

x : int |= x+ 1 : int∅ |= fun x -> x+ 1 : int→ int

...f : int→ int |= 2 : int

∅ |= let f = fun x -> x+ 1 in f 2 : int

On se doute qu’il y a un rapport entre bon typage et le bon comportementd’un terme PCF. Autre remarque : le combinateur Y (ou autre combinateur depoint fixe) ne type pas, c’est pourquoi on a introduit fix dans le langage.

On a le theoreme suivant, que l’on ne demontrera pas :

Theoreme 4 Si ∅ |= t : τ alors la reduction de t est infinie ou se termine surune valeur (le |= est decrit formellement apres : c’est l’inference que le terme ta le type τ)

On vient de donner les regles pour verifier les types. En fait, un langagecomme OCaml infere le type des termes, et pour ce faire, utilise des algorithmesparticuliers. Le probleme de l’inference de type est en general extremementcomplexe, car il est equivalent a trouver une preuve dans une certaine logique.

Les algorithmes communement utilises sont l’algorithme de Hindley (mono-morphe) et de Damas-Milner (polymorphe - a l’origine du typage Caml). Dansce dernier algorithme, tout terme (de Caml, ou de PCF, par exemple) a un typeprincipal (le plus general). L’algorithme est fonde sur l’unification de termes dupremier ordre (sorte de resolution d’equation dans une algebre libre de termes).La complexite de ce type d’algorithme est au pire exponentielle, mais en pratiqueelle est quasi lineaire, comme les programmeurs OCaml ont pu le constater (letypage est tres rapide en pratique). L’inference de types est une forme d’inferencede preuve (voir le calcul des sequents, section 7.5), comme on le montre, de faconun peu informelle, dans la section suivante.

9.7 Theorie de la demonstration et typage

Tout ceci est tres proche de la deduction naturelle, dans un fragment de lalogique du chapitre 8. En fait, c’est une presentation d’un fragment intuitionistepar un calcul de sequents. Pour s’en convaincre, oublions les termes PCF danscertaines regles de typages, un instant.

Reprenons la creation de fonctions = (⇒ Ig) ? :

Γ, A`BΓ`A→ B

Rappel (chapitre 8) :

(⇒ Ig)Γ ` A,∆ Γ ` B,∆

Γ, A⇒ B ` ∆

Page 129: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

9.7. THEORIE DE LA DEMONSTRATION ET TYPAGE 129

Donc oui, c’est la meme regle, avec ∆ = ∅.Maitenant l’affectation - (cut) ? :

Γ`A Γ, x : A`BΓ`B

Rappel (chapitre 8) :

(cut)Γ ` A,∆ Γ′, A ` ∆′

Γ,Γ′ ` ∆,∆′

Donc oui, c’est la meme regle, avec ∆ = ∅, Γ′ = Γ et ∆′ = B.Revenons a la regle de typage de la paire - (∧Id) ? :

Γ`A Γ`v : BΓ`(u, v) : A∧B

Rappel (chapitre 8) :

(∧Id)Γ ` A,∆ Γ ` B,∆

Γ ` A ∧B,∆

Prenons un exemple typique de cette correspondance type/formules, et preuves.Revenons aux produits un instant.

Une fonction de f : X × Y vers Z peut-etre consideree comme :(i) bien sur une fonction qui a un couple de valeurs (x, y), avec x ∈ X ety ∈ Y , renvoie f(x, y) ∈ Z

(ii) une fonction de X vers Y → Z, qui a un x dans X associe la fonctionpartielle fx : Y → Z telle que fx(y) = f(x, y)

(iii) un element de X×Y → Z (soit une fonction de () (unit) vers X×Y →Z)

Passer de (i) a (ii) est “naturel” et on le fait constamment en OCaml. On aune fonction (d’ordre superieur)

curry : ((X × Y )→ Z)→ (X → (Y → Z))

qui s’appelle la “curryfication”.En Caml, cela se programme de la facon suivante :

let curry f x y = f (x , y ) ; ;va l curry : ( ’ a ∗ ’ b −> ’ c ) −> ’ a −> ’ b −> ’ c = <fun>

On a egalement la de-curryfication :

let uncurry f (x , y ) = f x y ; ;va l uncurry : ( ’ a −> ’ b −> ’ c ) −> ’ a ∗ ’ b −> ’ c = <fun>

Que l’on peut utiliser par exemple comme suit :

let f (x , y ) = x+y and g = curry f ; ;va l f : i n t ∗ i n t −> i n t = <fun>va l g : i n t −> i n t −> i n t = <fun>

Page 130: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

130 CHAPITRE 9. TYPAGE, ET PROGRAMMATION FONCTIONNELLE

let f 5 = g 5 ; ;va l f 5 : i n t −> i n t = <fun>

let h x = function y −> f (x , y ) andi = function x −> function y −> f (x , y ) ; ;

va l h : i n t −> i n t −> i n t = <fun>va l i : i n t −> i n t −> i n t = <fun>

Une autre fonction “naturelle” est l’evaluation :

eval : (X → Z)×X → Z

qui a tout x de X, et toute fonction de X → Z associe eval(f, x) = f(x) dansZ. En Caml, cela se programme de facon evidente :

let eva l f x = f x ; ;va l eva l : ( ’ a −> ’ b ) −> ’ a −> ’ b = <fun>

Vous remarquerez la similarite avec la logique propositionnelle. Le paradigmeest celui des “proofs as programs”, dans une logique “constructive” au moins :

“Programme=preuve de son type”

Illustrons cela par un exemple simple. On rappelle les fonctions Caml sui-vantes :

let curry f x y = f (x , y ) ; ;va l curry : ( ’ a ∗ ’ b −> ’ c ) −> ’ a −> ’ b −> ’ c = <fun>

La fonction curry est en fait une “preuve” de :

((a ∧ b) =⇒ c) =⇒ (a =⇒ (b =⇒ c))

De meme pour l’“application” qui s’ecrit en Caml :

let apply = uncurry eva l ; ;va l apply : ( ’ a −> ’ b ) ∗ ’ a −> ’ b = <fun>

La fonction apply est une preuve du “modus ponens” :

((a =⇒ b) ∧ a) =⇒ b

Reprenons maintenant encore apply. Supposons qu’on ait les axiomes (=”com-binateurs”) eval et uncurry : on peut en deduire une preuve constructive dumodus ponens :

(uncurry) ((u =⇒ v) =⇒ w) =⇒ ((u ∧ v) =⇒ w)

en faisant u = (a =⇒ b), v = a, w = b d’ou :

(((a =⇒ b) =⇒ a) =⇒ b) =⇒ (((a =⇒ b) ∧ a) =⇒ b)

Page 131: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

9.8. POUR ALLER PLUS LOIN 131

Mais on sait par (eval) :

(eval) ((a =⇒ b) =⇒ a) =⇒ b

Donc :

(uncurry eval) ((a =⇒ b) ∧ a) =⇒ b

Cette preuve correspond a l’execution de la composition des fonctions uncurryet eval :

uncurry eva l ; ;− : ( ’ a −> ’ b ) ∗ ’ a −> ’ b = <fun>

De facon generale, on a la correspondance de Curry-Howard. Un programmede type t est une preuve de t comme suit : (en logique intuitioniste)

Terme logique Type informatiqueImplication type fonctionnelconjonction type produitdisjonction type somme

vrai type unitfaux ⊥ (exception/boucle infinie)

Les quantificateurs correspondent aux types dependants.

9.8 Pour aller plus loin

La plupart des cours permettant d’approfondir ce theme se trouvent auMPRI (en M2). Parmi les themes tres avances, on retrouve :

– la generalisation de la correspondance de Curry-Howard a la logique clas-sique (call-with-current-continuation, transformation continuation passingstyle)

– certains “grands” theoremes ont ete interpretes comme des programmes(ex. theoremes de completude et d’incompletude de Godel, forcing de Co-hen etc. - par Jean-Louis Krivine)

– des liens entre la topologie algebrique et certains systemes de types : cf.“Homotopical Foundations”de Vladimir Voevodsky (medaille Fields 2002),http://homotopytypetheory.org.

Page 132: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

132 CHAPITRE 9. TYPAGE, ET PROGRAMMATION FONCTIONNELLE

Page 133: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

Chapitre 10

Programmation reactivesynchrone

10.1 Introduction

Ce chapitre introduit a un paradigme de programmation original, la pro-grammation reactive synchrone (en particulier Lustre), egalement tres utile enpratique, par exemple pour le codage du controle commande d’avions, de cen-trales etc. Le code du calculateur primaire de vol de l’A380 par exemple, estecrit en Lustre. C’est aussi un langage de programmation a la semantique trespropre, qui permet d’illustrer encore les outils de la semantique du chapitre 6.

Ce paradigme vient en quelque sorte d’un mariage du controle et de l’informa-tique. En general, on peut decrire les programmes dans ce paradigme, graphi-quement, par schema-bloc (comme Matlab/Simulink le fait par exemple) maisegalement a travers un langage textuel. Ce langage est declaratif, un programmeest un ensemble d’equations, comme nous allons l’expliciter plus bas.

En pratique on trouve des versions academiques de tels langages, tels LUSTRE,et aussi industrielles : SCADE (Esterel Technologies). Ces langages ont une bellesemantique et ont ainsi souvent de nombreux outils associes, de preuve, test etc.

10.2 Lustre

Lustre est un langage de programmation declaratif, donne par un ensemble“d’equations”mutuellement recursives en general, calculant des suites de signauxde sortie, a partir de signaux d’entree. On peut voir la machine d’execution sous-jacente comme une machine parallele, dans lequel chaque equation (ou “noeud”)est un processus traitant des signaux cadences a un certain rythme temporel, eten renvoyant d’autres aux autres noeuds. La machine sous-jacente est donc ungraphe de processus, avec un modele d’execution tres simple : tous les processusont la meme horloge globale, c’est-a-dire qu’ils lisent leur entrees, calculent, et

133

Page 134: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

134 CHAPITRE 10. PROGRAMMATION REACTIVE SYNCHRONE

produisent leurs sorties tous en meme temps, a chaque “tic” d’horloge. Lustreimplemente en fait les reseaux de Kahn (on les introduit a la section 10.5)synchrones. Par synchrone, on entend le fait qu’un message par arc du reseauest envoye et recu a chaque “tic” de l’horloge globale. Cela permet d’eviterl’utilisation de tampons de communications potentiellement non bornes, avecune puissance de calcul similaire aux reseaux de Kahn generaux.

Un programme Lustre opere sur un flot, c’est-a-dire une suite de valeurs : unevariable x en Lustre represente une suite infinie de valeurs (x0, x1, . . . , xn, . . .) ;xi est la valeur de x au temps i. Un programme Lustre prend un flot et renvoieun flot et toutes les operations sont globales sur un flot :

– L’equation de flot x = e est un raccourci pour ∀n, xn = en

– L’expression arithmetique sur les flots x+ y renvoie le flot (x0 + y0, x1 +y1, . . . , xn + yn, . . .)

Lustre introduit egalement des operateurs temporels. Ceux-ci sont :– pre (precedent) qui donne la valeur au temps precedent, d’un flot argu-

ment : pre(x) est le flot (⊥, x0, . . . , xn−1, . . .).– -> (suivi de) est utilise pour donner des valeurs initiales d’un flot : x->y

est le flot (x0, y1, . . . , yn, . . .).Remarquez que les flots sont types. bool par exemple est le type des flots de

booleens.Les expressions arithmetiques et booleennes, syntaxiquement, sont les memes

que d’habitude, mais etendues point a point aux flots. On a egalement une formesyntaxique pour l’affectation : let ... = ... tel, les conditionnelles : if ...then ... else, et la sequence.

L’organisation d’un programme Lustre est faite ainsi. Un programme Lustreest un ensemble d’equations a priori potentiellement mutuellement recursives.Chaque equation est defini par un noeud identifie par le mot cle node. Uneequation ou noeud est une fonction prenant des flots en argument, renvoyant unflot en resultat.

Donnons pour premier exemple un programme simple, compteur d’evene-ments :

node Count ( evt , r e s e t : bool ) r e tu rns ( count : int ) ;l e t

count = i f ( true−>r e s e t ) then 0else i f evt then pre ( count)+1else pre ( count ) ;

t e l

Dans ce programme, true->reset est un flot booleen, egal a vrai a l’instantinitial et quand reset est vrai. Quand il est vrai, la valeur de count est renvoyeeegale a zero. Sinon, quand evt est vrai, on renvoie la valeur a l’instant precedentde count plus 1 ; sinon on conserve l’ancienne valeur.

La representation graphique associe a cette version textuelle du programmeest la suivante :

Page 135: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

10.2. LUSTRE 135

Voici un exemple d’utilisation de ce compteur d’evenements.

mod60 = Count ( second , minute ) ;minute = second and pre (mod60)=59;

Dans ce programme, mod60 est la sortie du noeud Count, qui compte lessecondes, et se remet a zero chaque minute. minute est vrai quand seconde estcadence et que sa valeur precedente est de 59.

Prenons maintenant un exemple du monde du traitement du signal : lesfiltres lineaires a reponse finie. Ce sont des calculs recurrents qui prennent uneentree a l’instant n, xn et renvoient en sortie, a l’instant n, yn donnee par :

yn =L−1∑m=0

bmxn−m

Graphiquement :

Prenons maintenant l’exemple des filtres lineaires a reponse infinie (filtres re-cursifs). Ils prennent en entree a l’instant n, xn, et renvoient en sortie a l’instantn, yn donnee par :

yn =L−1∑m=0

bmxn−m +M−1∑m=1

amyn−m

Graphiquement :

Page 136: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

136 CHAPITRE 10. PROGRAMMATION REACTIVE SYNCHRONE

Un code Lustre typique, correspondant, par exemple dans le cas ou la sortieest donnee par : yn = xn + 0.9yn−1 :

node f i l t e r ( x : r e a l ) r e tu rns (y : r e a l ) ;l e t

y = x+0.0 −> 0 .9∗ pre (y ) ;t e l ;

Prenons maintenant un autre exemple : celui du chien de garde (watchdog).Il permet de gerer des echeances : il emet alarm quand watchdog est en attenteet que deadline est vrai :

node WATCHDOG1( set , r e s e r t , d e a l d l i n e : bool ) return ( alarm : bool ) ;var watchdog is on : bool ;l e t

alarm = dead l ine and watchdog is on ;watchdog is on = f a l s e −> i f s e t then true

else i f r e s e t then f a l s eelse pre ( watchdog is on ) ;

a s s e r t not ( s e t and r e s e t ) :t e l ;

(les flots booleens set et reset ne doivent pas etre vrais en meme temps)Pour assurer la causalite, comme tout reseau de Kahn se doit, on doit ope-

rer des restrictions syntaxiques sur les programmes Lustre. Par exemple, letx=x+1 ; n’est pas un programme Lustre correct : le flot x depend instantane-ment de lui-meme, ce qui n’est pas possible (ou alors resolution d’equations, quine donnerait pas de solution ici). La condition syntaxique imposee ici est qu’unevariable recursive doit etre gardee par un delai. On ne peut pas ecrire les chosessuivantes :

x = x+1;

ni :

x = i f b then y else z ;y = i f b then t else x ;

Page 137: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

10.3. CALCUL D’HORLOGES 137

10.3 Calcul d’horloges

Lustre fournit egalement des moyen de faire un calcul d’horloges, c’est-a-dire, en particulier, de definir plusieurs horloges. Cela se fait entre autres parl’operateur de sous-echantillonnage when. Celui-ci permet de cadencer differem-ment des processus (=noeuds), mais toujours selon un multiple du temps debase. Par exemple, l’operateur de sous-echantillonage X when B, ou X est un flotquelconque, B un flot booleen donne dans le cas plus bas :

B false false true true false trueX X0 X1 X2 X3 X4 X5

Y=X when B X2 X3 X5

Ce calcul d’horloge repose egalement sur un operateur de surechantillonnagecurrent. Celui-ci permet d’injecter un flot lent dans un nouveau flot rapide(cadence au temps de base). Par exemple :

B false false true true false trueX X0 X1 X2 X3 X4 X5

Y=X when B X2 X3 X5

Z=current Y ⊥ ⊥ X2 X3 X3 X5

Remarque : au debut Z n’a pas de valeur ; on utilise souvent current ...->Yplutot que current Y

Considerons maintenant l’exemple suivant, du a Marc Pouzet. Il s’agit d’unadditionneur :

node somme( i : int ) r e tu rns ( s : int ) ;l e t s = i −> pre s + it e l ;

On a par exemple :

1 1 1 1 1 1 1cond true false true true false truesomme 1 1 2 3 4 5 6somme(1 when cond) 1 2 3 4(somme 1) when cond 1 3 4 6

Donc en general : f(x when c)6=(f x) when c ; de meme current(x whenc)6=x.

On pourrait vouloir ecrire :

l e t h a l f = true −> not ( pre h a l f ) ;o = x & (x when ha l f )

Que devrait-il se passer a la compilation ? Le code correspond au calculyn = xn&x2n. Il faudrait donc un mecanisme de passage de valeurs par buffersqui ici ne serait pas borne (n, . . . , 2n). Ceci est interdit pas un calcul d’horlogeinterne au compilateur.

Page 138: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

138 CHAPITRE 10. PROGRAMMATION REACTIVE SYNCHRONE

Pour ce faire, les horloges utilisees par un noeud doivent etre declarees etvisibles dans l’interface du noeud. Donnons un exemple de telle declarationd’horloge :

node s t a b l e s ( i : int )r e tu rns ( s : int ; ncond : bool ; ( ns : int ) when ncond ) ;

puis declaration d’horloges locales :

var cond : bool ;( l : int ) when cond ;

puis du code lui-meme :

l e tcond = true −> i <> pre i ;ncond = not cond ;l = somme( i when cond ) ;s = cur rent ( l ) ;ns = somme( i when ncond ) :

t e l ;

Les horloges et sous-horloges sont ainsi verifiees comme suit. Les constantessont cadencees sur l’horloge de base du noeud courant . Par defaut, les variablessont sur l’horloge de base du noeud. On a aussi clock(e1 op e2) = clock(e1) =clock(e2), clock(e when c) = c, et clock(current(e)) = clock(clock(e)).

Les horloges sont declarees et verifiees, il n’y a pas d’inference, tout estdeclare (ou regles implicites, voir plus haut). Deux horloges sont exactes si ellessont syntaxiquement egales.

10.4 Pour aller plus loin...

On peut verifier des proprietes temporelles, parlant d’evenements dans lefutur (“toujours dans le futur”ou“un jour dans le futur”), qui sont plus generalesque les invariants de la preuve a la Hoare : “Si a un instant n, x (=xn) est positif,alors il existe un instant m > n tel que pour tous les instants k ≥ m, y (=yk)est positif”. Une approche classique repose sur le fait que ces proprietes sontcodables en Lustre ! (processus “observateur” - model-checking etc.)

Un autre point qui pourra interesser les eleves motives, il existe un mariageentre le paradigme fonctionnel, et reactif synchrone : Lucid synchrone.

10.5 Reseaux de Kahn et semantique de Lustre

A l’origine de Lustre, on trouve une machine theorique formee d’un graphedont les noeuds traitent des informations envoyees d’autres noeuds a travers desfiles non-bornees, et qui renvoient sur les arcs sortant des messages a d’autresnoeuds.

Cette machine theorique abstrait en quelque sorte l’echantillonage et le trai-tement discret des donnees (automatique, traitement du signal etc.). Il va falloir

Page 139: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

10.5. RESEAUX DE KAHN ET SEMANTIQUE DE LUSTRE 139

neanmoins imposer une restriction sur le traitement fait par les noeuds pourque cela ait un sens.

Le domaine semantique que nous allons utiliser est le suivant. Le domainedes donnees S est celui des suites de valeurs (dans Val) finies (x0, . . . , xn) oupas (x0, . . . , xn, . . .). On identifiera la suite finie (x0, . . . , xn) avec la suite infiniea valeur dans Val ∪ ⊥ : (x0, . . . , xn,⊥, . . . ,⊥, . . .) donc S = x : N → Val⊥ |xi = ⊥ ⇒ (∀j ≥ i, xj = ⊥).

On definit alors l’ordre partiel prefixe sur S par, pour x, y ∈ S, x ≤ y si :

xi 6= ⊥ ⇒ yi = xi

Dit de facon plus simple, x est un prefixe de y ; et l’ordre prefixe est la restrictiona S de l’ordre defini au chapitre 6 pour N→ Val⊥ (Val⊥ etant un CPO). S estdonc un CPO (verification triviale).

Il faut maintenant definir les fonctions aux noeuds du graphe, d’un reseaude Kahn. On veut qu’elles soient calculables, on impose donc naturellementla continuite (cf. chapitre 6). En fait, on peut se contenter ici d’imposer pourf : Sn → Sm la commutation aux sup ; celle-ci peut s’imposer coordonnee parcoordonnee (on suppose ici m = n = 1) : pour toute ω-chaıne x0 ≤ x1 ≤ . . . ≤xj ≤ . . . de S,

f

⋃j∈N

xj

=⋃j∈N

f(xj)

(comme f(xj) n’est pas necessairement croissante, il faut supposer qu’il existeun sup de cette suite, egale au terme de gauche, dans cette definition)

Cette condition, historique, est en fait equivalente a la “continuite” dansnotre cas. En effet, pour toute ω-chaıne x0 ≤ x1 ≤ . . . ≤ xn ≤ . . . on a, pourtout j ∈ N :

(⋃i∈N f(xi)

)j

=

f(xk)j ∃k ∈ N, f(xk)j 6= ⊥et ∀l ≥ k, f(xl)j = f(xk)j

⊥ sinon=

f(⋃

i∈N xi)j

= f

(y →

xk

j ∃k′ ∈ N, xk′

j 6= ⊥⊥ sinon

)(x)

Intuitivement pour j fixe, la jieme valeur du flot de sortie de f est determineepar l’image par f sur un prefixe fini du flot d’entree. La continuite est ici unesorte d’axiome de “causes finies”.

Pour mieux comprendre l’importance de la condition de continuite, donnonsici un exemple de fonction non continue sur S. Soit g : S → S telle que :

g(x) =

(0, . . . , 0, . . .) si x est fini(1, . . . , 1, . . .) si x est infini

Soit y flot infini et yi, i = 0, 1, . . . , tous ses prefixes finis :⋃

i∈N yi = y maisg(⋃

i∈N yi) = (1, . . .) et⋃

i∈N g(yi) =⋃

i∈N(0, . . .) = (0, . . .).

Page 140: Principes des langages de programmation INF 321€¦ · Objectif du cours Ce cours de L3 vise a donner des el ements conceptuels concernant les langages de programmation. Il a et

140 CHAPITRE 10. PROGRAMMATION REACTIVE SYNCHRONE

A l’origine, Kahn demandait juste la preservation des bornes superieures.Quid de la croissance (qui est une forme de causalite) ? Supposons que l’on aitune fonction f : S → S telle que pour toute ω-chaıne x0 ≤ x1 ≤ . . . ≤ xj ≤ . . .,

f

⋃j∈N

xj

=⋃j∈N

f(xj)

Soit la suite x = (x0 = x, x1 = y, . . . , xn = y, . . .) ∈ S, alors f(⋃

j∈N xj)

= f(y)

mais⋃

j∈N f(xj) = z est tel que f(x) ≤ z = f(y) par hypothese, on a donc lacroissance.