Programmation Fonctionnelle et Symbolique - Notions de sémantique



6.a Quelques généralités

Terme de linguistique, la sémantique est (selon le dictionnaire Lexis), «l'étude du sens (ou contenu) des mots et des énoncés, par opposition à l'étude des formes (morphologie) et à celle des rapports entre les termes dans la phrase».

A la différence des langues naturelles, les langages de programmation sont des objets linguistiques relativement simples :

En informatique, la sémantique est l'étude de la «signification» des programmes. Comment définir formellement la signification d'un programme ? C'est assez simple en fait; prenons par exemple le programme suivant :

programme m donnée en entrée : x,y (entiers positifs) résultat en sortie : r (entier) début r := 0; tantque x>0 faire debut x:=x-1; r:=r+y; fin fin La signification de ce programme est tout simplement la fonction S[m] (S pour Sémantique) qui à deux entiers x et y fait correspondre un certain résultat r en fin de traitement : S[m]: N x N -> N. S[m][n,p] = n*p Plus généralement la signification d'un programme p est représentée par une certaine fonction S[p] : D -> R, qui à une donnéee fait correspondre un résultat. Cette fonction est souvent une fonction partielle, dans le sens où le calcul de P sur certaines données peut ne pas conduire à un résultat (le programme boucle, il y a eu une division par zéro, etc..). Le domaine de définition de S[p] est donc naturellement l'ensemble des données pour lesquelles le calcul se termine normalement.

Remarques :

Nota: Nous limitons ici à une classe très simple de programmes impératifs: programmes structurés, pas d'entrées-sorties, pas de procédures.

6.b Notion d'Environnement

Le petit programme donné en exemple va nous permettre de préciser quelques notions importantes. Tout d'abord la notion d'environnement : un environnement c'est l'association, à un moment donné de l'exécution de ce programme, de valeurs aux variables du programme.

Par exemple si on lance le programme pour x=2 et y=3, on a au départ
l'environnement initial e: x -> 2 , y -> 3
puis après l'initialisation e:x -> 2 , y -> 3 , r -> 0
et ensuite (dans la boucle) e: x -> 1 , y -> 3 , r -> 0
et encore .... e: x -> 1 , y -> 3 , r -> 3
etc... à la fin on aura e: x -> 0 , y -> 3 , r -> 6
Deux choses à remarquer :

6.c Sémantique de l'Affectation

Considérons l'instruction «r:=r+y». Nous voulons l'exécuter dans un certain environnement e. Comment fait on ? Tout simplement on additionne les valeurs de r et de y (prises dans l'environnement e) et on modifie la valeur de la variable r dans e; plus exactement on crée un environnement e1 semblable à e, sauf en ce qui concerne la valeur de r.

Nous avons besoin de définir une nouvelle fonction sémantique V qui renvoie la valeur d'une expression (si cette valeur existe) dans un certain environnement. Par exemple V[r+y](e) = 3

Par induction sur la structure des expressions :
Pour toute constante c : V[ c ](e) = c
Pour tout identificateur id :V[ id](e) = e(id) (si défini)
Pour toute somme exp+exp: V[ exp+exp](e) = V[ exp](e)+V[ exp](e) (si ces valeurs existent)
etc ... En général, si l'on a une affectation var := expr, la fonction sémantique S[var := expr] qui lui est associée est définie de la manière suivante : pour tout environnement e dans E :

S[var := expr](e) = si V[expr](e) existe alors e1 environnement tel que: e1(var) = V[expr](e) e1(id) = e(id) pour tout autre identificateur id.

6.d Sémantique de la Composition Séquentielle

Lorsqu'on sait faire une instruction, on peut légitimement essayer d'en faire deux à la suite l'une de l'autre! Interrogeons nous sur la séquence d'instructions « x:=x-1; r:=r+y»..

Supposons que nous l'exécutions en partant de l'environnement e. Après la première instruction on se retrouve dans l'env. e1 = S[x:=x-1](e); puis la seconde nous fait passer dans e2 = S[r:=r+y](e1). Donc:

S[x:=x-1;r:=r+y](e) = S[r:=r+y](S[x:=x-1](e)) = (S[r:=r+y] o S[x:=x-1])(e)\^ Pour résumer, si on a deux instructions i et i :
S[i; i] = S[i] o S[i]S
Simple composition de deux fonctions...

6.e Sémantique de la Répétition

Examinons maintenant la boucle «tant que». Une telle boucle comporte deux éléments : une condition et un corps. Nous savons, grâce à la fonction V, calculer la valeur d'une condition (c'est une expression booléenne). Nous savons également définir la signification du corps de la boucle. Comment recoller les morceaux ?

Soit <boucle> l'instruction : «tantque <cond> faire <corps>»

pour exécuter <boucle> dans l'environnement e, que fait on ?

Ce qui s'écrit : S[<boucle>](e) = si V[<cond>](e)=faux alors e si V[<cond>](e)=vrai alors S[<boucle>]( S[<corps>](e) ) Et voilà..

Exercices

6.f Conclusion

La programmation s'apprend en général par tâtonnements, ce qui fait parfois conclure un peu hâtivement qu'il s'agit d'un art (ou d'un artisanat, voire un bricolage) plutôt que d'une technique. Il convenait donc de montrer que l'édifice repose sur de robustes fondations mathématiques: composition de fonctions et calcul par récurrence. Qu'en conclure ?

  1. La notion d'environnement est fondamentale pour la compréhension de la programmation impérative. Cette notion ne fait pas partie du bagage mathématique usuel. D'où les réticences initiales à l'acceptation d'instructions du type : i:=i+1, qui violent l'apparence déclarative.
  2. toute tentative de compréhension -et d'explication- d'un programme non trivial (comportant par exemple une boucle) utilise nécessairement le raisonnement par récurrence, sous une forme plus ou moins apparente. C'est donc une technique qu'il convient de maîtriser.
  3. Ceci justifie l'apprentissage de la programmation fonctionnelle, à la fois comme langage de programmation familiarisant le programmeur avec le raisonnement explicite par récurrence, et comme formalisme descriptif/explicatif de la programmation impérative.

M. BILLAUD Département Informatique IUT-A Bordeaux (1989)