• Aucun résultat trouvé

Programmation fonctionnelle :

N/A
N/A
Protected

Academic year: 2022

Partager "Programmation fonctionnelle :"

Copied!
81
0
0

Texte intégral

(1)

M

A

REPUBLIQUE DU BENIN ---

UNIVERSITE D’ABOMEY-CALAVI ---

ECOLE POLYTECHNIQUE D’ABOMEY-CALAVI

Formation Doctorale « Sciences Pour l’Ingénieur »

MEMOIRE DE DEA/SPI Spécialité : Informatique

Présenté par :

Maurice S.H. COMLAN

Thème :

Soutenu le 09 août 2012 devant le jury composé de :

Prof DEGAN Gérard Président Dr DJOGBE Léopold Membre Dr FIFATIN François-Xavier Membre Dr HANGNILO Robert Membre

Dr SOGBOHOSSOU Médésu Membre (Maître de mémoire)

Programmation fonctionnelle : d'une formalisation

mathématique vers son implémentation naturelle et

rapide. Application au dépliage de réseau de Petri.

(2)

A mes parents A Anne

A tout le monde de la recherche

(3)

Remerciements

Nos sincères remerciements

Au Professeur David DELFIEU pour avoir proposé et encadré ce thème malgré la distance ;

Au Docteur Médésu SOGBOHOSSOU pour avoir joué l’intermédiaire entre nous et le Professeur DELFIEU et pour avoir suivi les travaux ici au Bénin. Nous vous disons merci pour tout ce que vous savez et surtout pour vos conseils ;

Au directeur de l’EPAC dont l’école nous a donné la formation qu’il faut d’abord pour le cycle d’ingénieur puis ensuite pour le DEA ;

A tout le personnel du LETIA et à son directeur pour nous avoir reçu en stage ; A tous les étudiants de DEA pour les échanges et le temps passé ensemble ; A toutes mes soeurs et amis pour le soutien sans cesse ;

A tous ceux qui d’une manière ou d’une autre ont contribué à la réalisation de ce mémoire.

Que DIEU vous garde et vous comble de ses bienfaits !

(4)

Table des figures

1.1 Modèle en cascade . . . 6

1.2 Cycle de vie en V . . . 7

1.3 La règle de transition . . . 8

1.4 Exemple de préfixe de dépliage . . . 10

1.5 Préfixe fini et complet du réseau de la figure 1.4(a) . . . 14

3.1 Interface de l’application Romeo . . . 34

3.2 Exemple de RdP édité sous Romeo . . . 35

3.3 Ensemble des librairies et fonctions du logiciel . . . 36

3.4 Réseau de Petri et le fichier par généré . . . 38

3.5 Exemple de RdP . . . 39

3.6 Exemple de réseau de Petri non pur . . . 45

3.7 Exemple de séquence non franchissable . . . 49

3.8 Exemple de réseau de Petri . . . 54

3.9 Dépliage du réseau de Petri de la figure 3.8 . . . 54

(5)

Résumé

La modélisation des systèmes temps réel représente la première phase de vérifi- cation de leurs contraintes temporelles. Il existe plusieurs méthodes de modélisation mais celle basée sur les places et les transitions, les réseaux de Petri, est l’une les plus utilisées. La deuxième phase est la réalisation pratique des tests de vérification.

Une première approche est la construction du graphe d’accessibilité qui énumère l’ensemble des états dans lequel le système peut se trouver. L’inconvénient de cette méthode est l’explosion combinatoire. Une solution pour retarder ce problème passe par la construction d’un préfixe complet et fini du dépliage des réseaux de Petri.

Ce dernier représente la deuxième approche de vérification. Les travaux présentés dans ce mémoire de DEA partent de la modélisation mathématique des systèmes sous forme de RdP et présentent une implémentation naturelle dans un langage de programmation fonctionnelle. Une application au dépliage des réseaux de Petri est réalisée.

Mots-Clefs : modélisation, vérification formelle, réseaux de Petri, dépliage, pro- grammation fonctionnelle, LISP, Romeo.

Abstract

Modeling real-time systems is the first phase of verification of their temporal constraints. There are several methods of modeling, but one based on the places and transitions, Petri nets, is one of the most used. The second phase is the practical realization of verification tests. One approach is the construction of the reachability graph that enumerates all the states in which the system can be. The disadvan- tage of this method is the combinatorial explosion. A solution to this problem is delaying the construction of a complete and finite prefix of the unfolding of Petri nets. This is the second audit approach. The work presented in this DEA depart from the mathematical modeling of systems in the form of Petri nets and have a natural implementation in a functional programming language. An application to the unfolding of Petri nets is performed.

Key words : modeling, formal verification, Petri nets, unfolding, functional Pro- gramming, LISP, Romeo.

(6)

Introduction

L’évolution croissante des techniques dans le domaine de l’informatique, que ce soit dans le sens des performances des éléments matériels ou logiciels ou dans le sens de la facilité de sa mise en œuvre, a fait que de nombreuses structures (entreprise, établissement scolaire, administration, etc) font appel aux systèmes informatiques.

En parallèle, depuis quelques années, nous sommes témoins d’une explosion dans le domaine de la communication en général. La communication est devenue le maître- mot dans le fonctionnement de toutes ces structures mais aussi dans notre quotidien.

Beaucoup de méthodes de travail ont été repensées autour de la communication [HP85].

Dans le domaine de l’informatique, la communication est souvent mise en œuvre pour faire coopérer les systèmes informatiques entre eux. Les systèmes sont dits distribués. Chaque entité du système distribué global peut parfois fonctionner de manière indépendante des autres entités tout en étant amenée à d’autres instants à fournir un service aux autres entités. Les exigences modernes font que ces services sont de plus en plus sollicités en temps réel. Une entité ne possède pas un temps indéfini pour fournir le service qui lui est demandé. Il est souhaitable qu’elle le fournisse dans un temps ou un intervalle de temps qui lui est imparti [Riv03].

Il est apparu alors deux domaines de recherche importants ces dernières années : le domaine des systèmes coopératifs et le domaine des systèmes temps réel. Chacun d’eux s’est attaché à trouver des solutions qui permettent de prendre en compte au mieux leur problématique : la coopération des entités distribuées pour le premier et la réalisation de tâches en respectant les échéances temporelles pour le second [Riv03].

Un système temps réel est un système qui interagit avec un environnement phy- sique en remplissant souvent des missions critiques pour lesquelles une faute du sys- tème peut avoir des conséquences graves. Il sera dit correct s’il possède les bonnes fonctionnalités et si celles-ci sont réalisées à temps, c’est-à-dire avec le respect des contraintes temporelles imposées par l’environnement ou par une certaine qualité de service offerte à un utilisateur. La validation fonctionnelle et temporelle des sys- tèmes temps réel est ainsi une nécessité forte. Toutes les situations, tous les com- portements du système doivent être envisagés pour que la validation fournisse des

(7)

résultats fiables.

Si l’on sait développer des systèmes temps réel (aller de la spécification vers la réalisation) il est indispensable, vu les conséquences possibles d’une faute tempo- relle, de porter un intérêt particulier à la sûreté de fonctionnement, aux méthodes permettant de valider le développement avec pour objectif de garantir à l’avance le bon fonctionnement du point de vue temporel [ALK04].

Afin de garantir le bon fonctionnement du système, il faut utiliser lors du dé- veloppement de ce système, des techniques de modélisation et outils formels (pos- sédant des représentations mathématiques que l’on peut associer à un programme) qui permettent d’effectuer des preuves de modèles. En ce qui concerne les méthodes formelles, elles consistent à traduire la réalité du système à concevoir en un mo- dèle mathématique abstrait. Partant de la description des systèmes, le modèle des réseaux de Petri (RdP) est un formalisme très répandu pour conduire de tels for- malismes. Une extension des RdP dénommée réseaux de Petri temporels permet de modéliser les spécifications temporelles attachées aux systèmes temps réel dur. De manière générale, les RdP présentent l’avantage essentiel de pouvoir modéliser les conflits, le parallélisme et la concurrence des actions du système en évitant une énu- mération de l’ensemble des états accessibles. En effet, une telle énumération est de nature explosive pour un système fortement concurrent. La conséquence est qu’une vérification basée sur la construction de l’espace d’état (même fini) d’un système complexe rencontre le problème de la limitation des ressources mémoires [Sog09].

L’une des solutions pour contourner le problème de l’explosion est la construction du préfixe partiel du dépliage du réseau.

La programmation est une étape presque incontournable pour la mise en place d’outils pour automatiser les différentes tâches de l’homme. Il existe trois paradigmes principaux de programmation à savoir le paradigme procédural ou impératif, le pa- radigme à objets et celui fonctionnel. Chaque paradigme de programmation pré- sente ses avantages et inconvénients. La programmation fonctionnelle semble être un meilleur choix pour le codage des formalismes mathématiques (définitions, pro- priétés, théorèmes, etc).

Le présent mémoire de DEA dont le thème est : Programmation fonction- nelle : d’une formalisation mathématique vers son implémentation natu- relle et rapide. Application au dépliage de réseau de Petri a pour objectif d’aborder un ensemble de méthodes permettant un codage naturel et rapide de dé- finitions, de propriétés et de théorèmes à l’aide d’un langage de programmation fonctionnelle.

Le mémoire est divisé en trois (03) chapitres. Le premier présente les généralités et la synthèse bibliographique autour des systèmes temps réel et de leurs formalismes par les réseaux de Petri. On abordera aussi dans ce chapitre la notion de dépliage

(8)

des réseaux de Pétri et des paradigmes de programmation. Le deuxième chapitre aborde la programmation fonctionnelle et la formalisation avec Lisp (langage de programmation). Le dernier chapitre présente les travaux réalisés (implémentation des définitions des RdP, dépliage des RdP).

(9)

Chapitre 1

État de l’art

Nous présenterons dans ce chapitre les concepts provenant des anciens travaux sur les systèmes temps réels. Nous définirons de façon formelle les systèmes temps réel et les réseaux de Petri et finirons par le dépliage des réseaux de Petri. Tous ces concepts nous aideront dans la suite de ce travail. Aussi et pour finir, les paradigmes de programmation sont abordés.

1.1 Systèmes temps réel

Les systèmes informatiques sont généralement classés suivant trois catégories [HP85] :

- les systèmes transformationnels transforment des données pour produire un ou plusieurs résultats. Les programmes de calcul scientifique sont des exemples de sys- tèmes transformationnels. Les résultats produits dépendent uniquement des données traitées ;

- les systèmes interactifs interagissent avec leur environnement. Leur exécution est contrôlée par une boucle d’attente des événements auxquels sont associés des traite- ments. Pour ces systèmes, l’environnement est généralement limité à un utilisateur humain. Les programmes de bureautique sont des exemples de systèmes interactifs.

Les résultats produits dépendent des données et des événements ;

- les systèmes réactifs réagissent aux stimuli émis par leur environnement. A la différence des systèmes interactifs, l’environnement évolue de manière indépendante et n’attend pas la fin du traitement associé à un stimulus émis. Les systèmes de contrôle-commande sont des exemples de systèmes réactifs. Les résultats dépendent des données, des événements et de leur enchaînement en liaison avec l’état de l’en- vironnement.

Dans cette section, nous nous intéresserons aux systèmes réactifs et plus précisé- ment aux systèmes réactifs temps réel.

(10)

1.1.1 Définition

Les systèmes temps réel sont une classe de systèmes informatiques réactifs dont le but est de suivre et piloter les évolutions d’un procédé dynamique avec des contraintes de temps[ELL+97]. Le temps de réaction du système aux stimuli du procédé doit être assujetti à la dynamique du procédé. La correction d’un système temps réel ne s’évalue donc pas uniquement du point de vue fonctionnel (justesse des résultats calculés) et temporel (enchaînement correct des événements) mais éga- lement du point de vue temps réel (dates d’arrivée des événements) : “ Un résultat juste mais hors délai est un résultat faux ” [Sta88]. Mais l’auteur de cet article ne nous demande pas de confondre un système temps réel avec un système rapide. Un évènement trop précoce avant sa date minimale d’intervalle est aussi un résultat faux.

Ces systèmes sont aujourd’hui présents dans des domaines tels que l’automobile, l’avionique, le spatial mais aussi le contrôle de chaînes de production, de processus chimique, etc. Le caractère critique souvent attaché aux systèmes temps réel résulte principalement du souci de préserver le procédé (par exemple un engin spatial), son environnement (par exemple une centrale nucléaire) et les personnes (par exemple un engin de transport). En conséquence, il est nécessaire d’appliquer des techniques de validation tout au long du processus de développement de tels systèmes [SFC98].

La validation doit aussi bien porter sur les contraintes fonctionnelles que sur les contraintes temporelles et sur les contraintes temps réel.

1.1.2 Les contraintes temps réel

Dans le cycle de vie d’un système temps réel, les contraintes temps réel appa- raissent dès la phase d’expression des besoins. Ainsi, lorsque sont spécifiées les fonc- tionnalités du système liées au contrôle du procédé, les contraintes temps réel néces- saires à la garantie de ce contrôle doivent être données. En fait, les systèmes réactifs temps réel, sont vus comme un ensemble d’actions qui opèrent sur des données en prenant en compte l’écoulement du temps et des événements qui apparaissent au ni- veau du système réactif ou de son environnement [Mam98]. De ce fait, les contraintes temps réel doivent être exprimées sur les principaux éléments (les actions, les évè- nements et les données).

1.1.3 Méthodologies de développement des systèmes temps réel

Le modèle du cycle de vie d’une application est un modèle des étapes ou des acti- vités qui commencent quand le logiciel est conçu et se termine quand le produit n’est plus utilisé [Meu99]. Le cycle de développement d’une application comprend typi- quement une étape d’expression des besoins, une étape de spécification, une étape de conception, une étape d’implémentation, une étape d’intégration et de test, une

(11)

étape d’installation et de vérification ainsi que des étapes d’opérations et de main- tenance. Selon le cycle de développement choisi, ces étapes ou activités peuvent survenir une ou plusieurs fois dans un ordre prédéterminé. Ces étapes font partie de tous les cycles de développement de systèmes indépendamment de la nature, du domaine, de la taille et de la complexité du système à développer. Plusieurs modèles de cycle de développement d’une application existent : le modèle en cascade [SK00], le cycle de vie en V [Som88], le prototypage rapide [Roy87], le prototypage évolu- tif [GS81], la réutilisation de logiciel [Gid84], le développement incrémental [Hir85], etc. Nous présenterons rapidement les principes des modèles classiques que sont le modèle en cascade et le cycle en V.

Le modèle en cascade

Le modèle en cascade (Figure 1.1 [SK00]) décrit le cycle de vie comme une succes- sion d’étapes conduisant à raffiner des niveaux de description du problème jusqu’à sa réalisation, en partant de la définition jusqu’à l’exploitation et à la maintenance.

Chaque étape est liée à l’étape suivante pour représenter le raffinement, et à l’étape précédente pour représenter les corrections par retour-arrière. A chaque étape est associée une phase de vérification ayant pour but de s’assurer de la conformité de la solution retenue avec les spécifications en entrée de l’étape. Un défaut de confor- mité implique de reprendre l’étape ou de revoir le résultat de l’étape précédente. Ce modèle s’appuie sur le fait qu’un accroissement important de l’effort de validation durant les premières étapes favorise une correction rapide des premières erreurs et réduit le coût de correction considérable lors de l’implémentation.

Figure1.1 – Modèle en cascade

Le modèle cycle de vie en V

Le modèle cycle en V considère la vérification et l’évaluation du système à chaque

(12)

étape de réalisation. Il montre que la démarche de spécification/conception est glo- balement descendante tandis que la phase de réalisation/vérification est globalement ascendante. Il s’agit alors d’assembler les constituants pour obtenir les fonctionnali- tés souhaitées. Le cycle de vie en V du génie logiciel (Figure 1.2) [Som88], a largement inspiré le cycle de vie des Systèmes Automatisés de Production.

Figure1.2 – Cycle de vie en V

L’approche par étapes est adaptée aux systèmes temps réel car ce sont des sys- tèmes dont les besoins sont figés et clairement identifiables. En effet, lorsqu’un sys- tème est destiné à piloter un procédé, l’analyse des besoins se déduit naturellement des lois de commande conçues pour assurer ce pilotage et des caractéristiques phy- siques de l’installation à contrôler. De plus, en identifiant clairement les débuts et fins des différentes étapes, une approche par étape est statique et donc bien adaptée à la certification (ce qui est une nécessité pour la majorité des systèmes critiques).

1.2 Les Réseaux de Petri

Les réseaux de Petri (RdP) [Pet62] constituent un modèle fondamental pour la modélisation de systèmes à événements discrets. Ils sont utilisés dans de nombreux domaines, notamment dans les milieux industriels, et sont adaptés à la modélisa- tion de systèmes distribués, ou répartis. Les applications sont diverses et couvrent les domaines des systèmes de production, de transports et de communication. De nombreuses extensions du modèle ont été proposées, et en particulier des extensions adaptées à la modélisation de systèmes temporisés. Dans cette section, nous présen- terons le modèle des réseaux de Petri, accompagné des définitions nécessaires.

Définitions : Un réseau de Petri [Pet62, Mur89] est un graphe orienté valué avec

(13)

deux types de nœuds : les places (représentées par des cercles) et les transitions (représentées par des barres). Un arc orienté relie une place à une transition, ou inversement une transition à une place. Les arcs portent chacun une étiquette nu- mérique appelée poids de l’arc (de valeur 1 par défaut ou omis). Le plus souvent, une place modélise l’état d’un objet ou d’une ressource du système, et une transition modélise un événement ou une action dans le système. Les places peuvent contenir un ou plusieurs jetons (représentés par des points). Un jeton représente un état actif pour l’objet modélisé par la place qui le contient. Une multiplicité de jetons dans une place indique souvent le nombre d’exemplaires d’une ressource. Le marquage cou- rant du RdP correspond au dénombrement de jetons dans l’ensemble de ses places et définit l’état du système modélisé à un instant donné [Sog09].

Une transition du RdP est dite franchissable (ou tirable) lorsque toute place reliée à un de ses arcs entrants (chaque place est appelée place précédente de la transition) contient un nombre de jetons supérieur ou égal à la valuation ou poids de cet arc entrant. Une transition sans place précédente (transition source) peut être librement franchie à tout instant. Lorsqu’une transition du RdP est franchie (pour symboliser l’occurrence d’un événement ou action), le marquage est modifié en retranchant à chaque place précédente de la transition un nombre de jetons égal à la valuation de l’arc entrant correspondant, puis en ajoutant à chaque place suivante de la transition (c’est-à-dire une place reliée à un arc sortant de cette transition) un nombre de jetons égal à la valuation de l’arc sortant correspondant. Le marquage ainsi modifié représente un nouvel état du système. Le franchissement en séquence de transitions modélise la dynamique du système par des changements successifs de son état dans l’échelle temporelle. La figure 1.3 représente un exemple de RdP en (a) et le même réseau après le tir de la transition t en (b) (source [Sog09]).

t 2

(a) le réseau avant le tir de la transition t

3

p1 p2

p3 p4 p5

t 2

(b) le réseau après le tir de la transition t

3

p1 p2

p3 p4 p5

Figure1.3 – La règle de transition

Plus formellement, un réseau de Petri est un quadruplet R =< P, T, P re, P ost > où :

P : ensemble fini de places, T : ensemble fini de transitions,

P re : P ∗T −→N est l’application définissant les places précédentes,

(14)

P ost : P ∗T −→N est l’application définissant les places suivantes.

On utilise également la notation : C = P ost−P re et C est appelée la matrice d’incidence du réseau de Petri.

Nous nous limiterons dans ce document aux réseaux de Petri non temporisés.

Aussi les travaux [Sog09] offrent plus de détails sur les RdP et aussi sur leurs exten- sions temporelles. Nous reviendrons sur les réseaux de Petri dans le chapitre 3 lors de la modélisation et de l’implémentation de ces réseaux dans un langage fonctionnel.

1.3 Dépliage des réseaux de Petri

Le dépliage d’un réseau marqué est le fait de produire successivement les évè- nements possibles à partir de son marquage initial. Les premiers travaux sur le dépliage ont envisagé une sémantique d’ordre partiel aux réseaux de Petri [NPW81].

L’objectif des méthodes par ordre partiel concerne l’optimisation des méthodes de vérification en tenant compte des notions d’entrelacement et de concurrence des évé- nements d’un système. Ce processus de branchement est un autre réseau de Petri dont les places sont appelées les conditions et les transitions, les évènements.

Pour un réseau de Petri, le nombre d’événements du dépliage est infini dès qu’une séquence infinie de franchissements est réalisable. La méthode de vérification que nous présentons ici, repose sur le calcul d’une partie du dépliage du réseau, appelée préfixe fini. Une des difficultés majeures de cette méthode est de s’assurer qu’un préfixe fini est suffisamment grand pour permettre la vérification d’une propriété donnée. [McM92, ERV96] proposent une méthode de construction d’un préfixe fini adapté à la détection d’états bloquants et plus généralement à la vérification de cer- taines propriétés d’accessibilité. De nombreux auteurs [Esp94, CP96, CP99, Gra97, EH00] ont développé des méthodes pour la vérification de propriétés de vivacité, et plus généralement de propriétés de logique temporelle.

L’exemple de la figure 1.4(b) [Sog09] montre un préfixe du dépliage (dépliage inachevé) ou partie initiale de dépliage du réseau en (a).

1.3.1 Définitions sur les réseaux d’occurence

Un réseau d’occurrence O =< B,E,F > est un réseau de Petri ordinaire avec B l’ensemble des conditions, E l’ensemble des événements, et F la relation de flux (F ⊆ B × E ∪ E × B) tel que : |b| ≤ 1 (∀b ∈ B), e 6= ∅ (∀e ∈ E), et F+ (la fermeture transitive de F) est une relation d’ordre strict (irréflexive et transitive), d’où O est acyclique.

On définit trois types de relations entre les nœuds d’un réseau d’occurrence :

(15)

p3

(a) p4

t3 t4

t1 t2

(b)

p1 p2

(p3) (p4)

(t3) (t4)

(t1) (t2)

(p1) (p2)

(p4) (t2) (p2)

(p3)

(t3) (t4)

(t1) (p1) p5

(p5) (p5)

(p1) (p5)

(t4) (p5)

(t4)

b1 b2 b3

b4 b5 b6

b9 b8 b7

b10

b13 b11 b12

e1 e2 e3

e6 e5 e4

e7

e10 e8 e9

Figure 1.4 – Exemple de préfixe de dépliage

– pourx, y ∈ B ∪ E, nous définissons larelation causale stricte ≺telle que :x≺y ssi (x, y)∈ F+;

– ∀b ∈ B, si e1, e2 ∈ b ⊆ E (e1 6= e2), alors e1 et e2 sont en relation de conflit (relation symétrique, irréflexive et non transitive), ce qui est noté e1 ] e2. De plus, si e1 ] e2, pour x, y ∈ B ∪ E tel que e1 ≺x et e2 ≺y, nous avons : x ] y, x ] e2 ete1 ] y.

– xoy ssi¬((x≺y)∨(y≺x)∨(x ] y)):o est larelation de concurrence (relation symétrique, irréflexive et non transitive).

Deux nœuds quelconques d’un réseau d’occurrence vérifient une et une seule de ses trois relations (les trois relations s’excluant mutuellement).

Un réseau marqué N =<P,T,W, m0 >admet U nf =<O, λ > commeproces- sus de branchement (ou préfixe de dépliage) si :

– O =<B,E,F > est un réseau d’occurrence ;

– λ est une application (dénommée fonction d’étiquetage) telle que λ :B ∪ E → P ∪ T. Elle possède les propriétés suivantes :

– λ(B)⊆ P, λ(E)⊆ T ;

– ]b∈M in(O)λ(b) =m0 (l’union] étant définie sur le multi-ensemblem∈ M(P) des jetons formant un marquage) ;

– pour tout e∈ E, la restriction de λ à e (resp. e) est une bijection entre e

(16)

(resp. e) etλ(e) (resp. λ(e)). Nous avons λ(e) =λ(e) et λ(e) =λ(e), ce qui signifie queλ préserve l’environnement d’une transition.

Ainsi, U nf est un réseau d’occurrence étiqueté : chaque place de B porte une éti- quette de nom dansP, et chaque transition de E, une étiquette de nom dans T.

Une coupe est un ensemble de conditions toutes en relation o par paire (concur- rence mutuelle). Si B0 est une coupe (B0 ⊆ B), elle vérifie donc la propriété :

∀b, b0∈B0, b6=b0 ⇒bob0.

Une B-coupe est une coupe maximale au sens de la relation d’inclusion ; dans un processus de branchement, elle correspond à un marquage accessible du réseau mar- quéN. SiB0est une B-coupe (B0 ⊆ B), elle vérifie donc la propriété supplémentaire : b /∈B0 (b∈ B)⇒ ∃b0 ∈B0 t.q. b≺b0∨b0≺b∨b ] b0 [Sog09].

1.3.2 Algorithme du dépliage

Nous donnons ici une procédure à appliquer pour obtenir le dépliage d’un réseau ordinaire initialement marqué [Sog09].

Les nœuds deU nf =<O, λ >peuvent être codés comme suit : une conditionbm est un couple composé d’un label pi (la place correspondante dans le réseau N) et un pointeur vers l’unique événement précédent en de la condition : bm = (pi,@en); les conditions minimales ont un pointeur nul. Un événement en est également un couple composé d’un label, la transition correspondante dans le réseau N, et un ensemble X des pointeurs vers les conditions précédentes de l’événement : ei = (tj, X). Les conditions (resp. événements) sont contenus dans la table B (resp. E).

Puisque les informations des arcs sont implicitement codées dans la représentation des événements et des conditions,U nf peut être redéfini comme une union :U nf = B∪E.

Les compteurs m et n sont associés respectivement aux deux tables B et E : chaque fois qu’une condition (resp. un événement) sera créée, incrémenter le comp- teur correspondant. La construction du dépliage de <N, m0 > s’obtient pratique- ment par l’algorithme suivant :

1. Initialement :B :=∅, E :=∅, m:= 1, n:= 1.

2. créer une conditionbm := (pi, N IL)pour chacun des|m0|jetons (m ∈[1..|m0|]) du marquage initial (pi étant la place correspondante du jeton), et rajouter bm à l’ensemble B : B :=B∪ {bm};

3. pour toute coupe B0 ⊆ B, s’il existe tj ∈ T tel que (λ(B0) = tj)∧(@ek ∈ E,ek =B0∧λ(ek) = tj), alors :

(a) créer l’événement en := (tj, X) (X étant l’ensemble des pointeurs sur les pré-conditions contenues dans B0) tel que B0 =en : E :=E∪ {en}; (b) créer une condition bm := (pi,@en) par place pi ∈ tj, et rajouter bm à

l’ensemble B :B :=B∪ {bm}.

(17)

L’étape3est répétée tant qu’il existe un nouvel événementen possible. Le processus peut donc s’exécuter indéfiniment si <N, m0 > permet un ou plusieurs comporte- ments infinis.

1.3.3 Préfixe fini et complet

McMillan [McM92, McM95] propose un algorithme (formalisé et amélioré dans [ERV96]) pour construire un dépliage fini d’un réseau ordinaire borné, le processus se terminant quand le dépliage contient tous les marquages accessibles.

Soit un dépliage U nf =< O, λ > d’un réseau marqué < N, m0 >, avec O =<

B,E,F >. Nous définissons F comme la fermeture transitive et réflexive de F. Soient e, e0 ∈ E : la relation causale non stricte 4 est telle que e0 4e est équivalent à(e0, e)∈ F. Un ensemble d’événements C (C⊆ E) forme une configuration ssi :

– ∀e ∈C:∃e0, e04ee0C (C est causalement fermé) ; – ∀e, e0∈C : ¬(e ] e0)(C est sans conflit).

Une configuration constitue donc le processus d’un réseau causal.

SoitC, une configuration.Cut(C)désigne le marquage atteint par la configuration (c.-à-d. après avoir produit exactement l’ensemble des événements deC à partir du marquage initial) :

Cut(C) = (M in(O)∪C)\C

On notera M ark(C), le marquage correspondant : M ark(C) =]b∈Cut(C)λ(b).

Définitions

U nf est ditcomplet[ERV96] si, pour tout marquage global accessiblemdu réseau marqué <N, m0 >, il existe une configuration C dans U nf tel que :

1. M ark(C) = m (c.-à-d. que m est représenté dans U nf), et

2. pour toute transition t sensibilisée par m, il existe une configuration C ∪ {e}

dans U nf telle que :e /∈C∧λ(e) = t.

Pour un réseau marqué < N, m0 > présentant des comportements infinis,nous nous intéressons au calcul d’un préfixe complet et fini contenant autant d’informa- tions (marquages et possibilités de tirs) que le dépliage du réseau. Un tel préfixe existe toujours pour un réseau marqué<N, m0> borné.

En pratique, la terminaison du calcul d’un préfixe complet et fini est basée sur la notion de configuration locale. Le terme « configuration » est alors remplacé par

« configuration locale », et le terme « marquage global » par « marquage partiel ».

La configuration locale d’un événement e ∈ E, notée [e], est l’ensemble des évé- nements e0∈ E tel que e0 4e : [e] ={e0 ∈ E |e04e}.

La notion d’événement cut-off permet d’arrêter la dérivation de nœuds à par- tir d’un événement e donné du dépliage, parce que la construction correspondante

(18)

est revenue dans un état partiel (M ark([e])) déjà identifié par un événement e0 (M ark([e]) = M ark([e0])) tel que λ(e) = λ(e0). En effet, il n’est plus nécessaire de calculer des successeurs pour e si ces derniers peuvent s’identifier à ceux de e0. On peut alors considérer un cut-off comme un événement « de trop » qu’on peut se passer de représenter, un autre événement déjà identifié dans le préfixe du dépliage lui étant équivalent.

L’algorithme présenté ci-après calcule le préfixe complet et fini d’un dépliage.

La procédure P E() ajoute les nouvelles extensions possibles (des événements) à

Entrées: un réseau de PetriN avec le marquage initialm0={p1, p2, ..., pn} Sorties : Le dépliageU nf de<N, m0>et les événements cut-offs

début

B:={b1, ..., bn} avec chaque condition ayant la formebi= (pj, N IL);

pe:=∅; co:=∅;

P E();

tant quepe6=faire

Choisir un événementedanspetel que ∀e0pe,|[e]| ≤ |[e0]|;

pe:=pe\{e};

sieest cut-off alors co:=co∪ {e};

sinon

E:=E∪ {e} (c.-à-d. ajouter l’événemente= (t, X)àE avecX l’ensemble des pointeurs vers les pré-conditions de e);

B:=Be (c.-à-d. créer les post-conditionsbi = (pj,@e)deeet les rajouter à B);

P E();

fin fin

U nf :=BE;

fin

PROCEDURE P E() début

tant que∃t∈ T ∧ ∃B0B | (λ(B0) =t B0 est une coupe (@e0 Epeco | e0=B0λ(e0) =t))faire

e:= (t, X)(avecX l’ensemble des pointeurs sur chaque condition deB0);

pe:=pe∪ {e};

fin fin

Algorithme 1:Calcul de préfixe de dépliage complet et fini

l’ensemblepe chaque fois que de nouvelles conditions (places) sont créées.

La production en priorité des événements de plus petite taille de configuration locale répond à la notion d’ordre adéquat défini dans [ERV96]. Lorsqu’un événement cut-off est détecté (e ∈ pe est cut-off si ∃e0 ∈ E, M ark([e]) =M ark([e0])∧λ(e) = λ(e0)∧e0 ≺ e), ses post-conditions ne sont pas produites dans B afin de ne pas en dériver de nouveaux événements. Le processus de dépliage se termine lorsque pe devient vide, ce qui signifie qu’il n’existe plus de nouvelles extensions possibles qui soient dérivables de B.

(19)

(p3) (p4)

(t3) (t4)

(t1) (t2)

(p1) (p2)

(p4) (t2) (p2)

(p3) (t1)

(p1) (p5) (p5)

(t4)

b1 b2 b3

b4 b5 b6

b9 b8 b7

b10

e1 e2 e3

e6 e5 e4

e7

Figure1.5 – Préfixe fini et complet du réseau de la figure 1.4(a)

La figure 1.5 donne le préfixe fini et complet du réseau de la figure 1.4(a). L’évé- nemente7 est cut-off et s’identifie àe1 :M ark([e7]) =M ark([e1]) ={p3} ∧λ(e7) = λ(e1) =t1∧e1 ≺e7.

1.4 Les paradigmes de programmation

Un programme est un ensemble d’instructions écrit dans un langage de program- mation pour réaliser une tâche bien définie. Il existe plusieurs manières de program- mation (désignées par paradigmes de programmation). Nous parlerons dans cette section de trois paradigmes. Chacun ayant ses avantages et inconvénients.

1.4.1 Le paradigme procédural

Le paradigme procédural (ou impératif) est le plus ancien. Apparu avec Fortran, poursuivi avec Pascal et C, il met en avant l’appel de procédure pour effectuer un calcul séquentiel. La programmation impérative considère qu’un texte de programme est une suite d’instructions pilotant un ordinateur, au contraire de la programmation fonctionnelle pure, où la notion d’instructions n’existe pas plus qu’en mathématiques.

L’itération est le mécanisme central pour effectuer un calcul répétitif, la récursivité y est rarement optimisée. Après Pascal qui fut le chantre de la programmation struc- turée des années 1970-90, c’est actuellement le langage C qui en est le représentant largement majoritaire. Il reste un langage incontournable pour les professionnels qui travaillent au plus près de l’architecture de la machine et du système d’exploitation.

Souvent présenté comme un langage machine de haut niveau, nombre de compila- teurs se contentent à l’heure actuelle d’effectuer une traduction vers C. Le langage

(20)

C n’a pas été tenu à l’écart de la tendance forte vers la programmation par objets, avec C++ (ou Objective C chez Apple, et C# chez Microsoft).

1.4.2 Le paradigme à objets

Le paradigme à objets est d’apparition ancienne (1967), il émergea réellement avec Smalltalk au milieu des années 70, mais n’a vraiment été popularisé qu’avec les langages Java et C++ dans les années 1990. Le mot d’ordre est la ré-utilisation du lo- giciel et son extensibilité. Pour ce faire, ces langages s’appuient sur la notion de classe et d’objet (analogue à la terminologie d’ensemble et d’élément en mathématiques).

Les objets ont du savoir-faire sous la forme de méthodes qui ne sont autres que des procédures mais destinées à être transmises à un objet receveur sous la forme d’un message. Le calcul est donc décentralisé dans les objets, dont les méthodes peuvent être fonctionnelles ou impérative. Qui plus est, certaines méthodes, dites statiques en Java par exemple, sont propres à la classe et non à l’objet, permettant ainsi une programmation procédurale classique à la C. La quasi totalité des langages majeurs actuels possèdent des objets (C avec C++ et Objective-C, Caml, Scheme, Python, etc), ceux-ci étant devenus la pierre angulaire du développement logiciel moderne.

1.4.3 Le paradigme fonctionnel

Le paradigme fonctionnel est la face puriste de la programmation. Parfois vu comme un lambda-calcul appliqué, nous pouvons raisonner comme en mathéma- tiques, faire des démonstrations par récurrence, des analyses récursives de com- plexité, et acquérir une rigueur certaine. Les fonctions récursives sont au cœur de ce paradigme. Les langages les plus courants sont Lisp, Scheme et Caml. Nous reparle- rons de la programmation fonctionnelle car c’est autour de ce paradigme que tourne ce travail.

Conclusion partielle

Le chapitre 1 nous a permis de définir les concepts autour des systèmes, de leurs modélisations et surtout de leurs vérifications. Les réseaux de Petri constituent l’une des meilleures méthodes de modélisation et la construction d’un préfixe fini du dé- pliage constitue une étape importante pour procéder à des tests de vérification. La programmation des définitions et propriétés pour l’automatisation des tâches peut se faire à l’aide de différents paradigmes. Celui basé sur les fonctions, programmation fonctionnelle, est le meilleurs choix pour les formalisations mathématiques. C’est ce que nous essayerons de montrer dans le chapitre qui suit.

(21)

Chapitre 2

Programmation fonctionnelle

La programmation fonctionnelle est abordée dans ce chapitre. De la formalisation en Lisp aux structures de contrôle de données en passant par les structures de don- nées natives de Lisp, nous essayerons de montrer que Lisp permet un codage simple des définitions mathématiques.

2.1 Lisp et formalisation

Lisp est un acronyme de LISt Processor et a été conçu par John MC Carthy [McC60] pour le traitement d’expressions symboliques. Dès son origine, il a été uti- lisé pour écrire des programmes de calcul symbolique différentiel et intégral, de théorie des circuits électriques, de logique mathématique, etc. Lisp est le symbole de la programmation fonctionnelle ; qui a pour concept de base les fonctions. Toute sa puissance réside dans son fonctionnement même et les structures de données qu’il manipule. Le concept de la récursivité est au cœur même de Lisp et les définitions récursives d’entités abstraites en Lisp correspondent à des définitions inductives ma- thématiques.

Exemple : Soit à faire la somme des carrés des entiers entre 1 et N. La forma- lisation mathématique nous donne :

SommeCarree=PN i=1i∗i

Dans un langage inpératif (Exemple en C) le calcul donne ceci : Code C (impératif )

int SommeCarree(int n){

int i=1 ,somme=0 ; while(i<=n){

somme+=i*i;

i++;

(22)

}

return somme;

}

De manière récursive, C permet de réaliser plutôt ce code de la manière suivante : Code C (Récursif )

int SommeCarree(int n){

if(n==1) return 1 ;

else return n*n + SommeCarree(n-1) ; }

En Lisp, cette définition donne : Code Lisp

(define SommeCarree(n)

(if (= n 1) 1 (+ (* n n)(SommeCarree((- n 1)))))

Cette dernière définition en Lisp nous permet de nous affranchir de la déclaration habituelle des variables dans les autres langages (C, C++, Java, ...) et de leurs ty- pages très forts. Aussi cette définition est très proche de la définition mathématique.

Autre avantage de la programmation avec Lisp est bien la formalisation qu’on y fait. Ceci passe par l’existence des mécanismes internes permettant d’apporter au programmeur des structures formelles natives. La notion de paire correspond à la définition mathématique de couple et d’une manière plus large, les listes corres- pondent à la notion d’ensemble en mathématique. Les listes nous évitent de déclarer des tableaux (couteûx en mémoire) ou des listes chaînées comme cela est courant dans les autres langages de programmation tels que C++ ou Java. Il existe d’autres structures de données à l’instar des vecteurs et tables de haching qui permettent de réaliser des codes plus proches des définitions mathématiques. Toutes ces structures seront abordées dans la suite de ce chapitre.

La mise en place des quantificateurs existentiels et universels permettant de ba- layer les ensembles est très simple en Lisp grâce à des structures de contrôle très simples à déployer. L’itération for permet de faire ce parcours. f or/or permet de mettre en place la définition mathématique “il existe” (∃) et f or/and pour quelque soit (∀).

Exemple

(for/or ([i ’(0 2 3 5 8)])(> i 5))

permet de vérifier si Pour i∈ {0 ; 2 ; 3 ; 5 ; 8} i>5

(23)

(for/and([i ’(12 -3.7 8 -15)])(> i 0) permet de vérifier si∀ i∈ {12 ; -3.7 ; 8 ; -15} i>0

Un autre point fort de Lisp est sa gestion des fichiers, des répertoires et des fe- nêtres quelque soit le système d’exploitation ; ce qui le rend multiplateformes.

2.2 Programmation fonctionnelle

Cette section n’a nullement la prétention de vous apprendre tout sur la program- mation fonctionnelle, mais veut juste aborder le nécessaire pour comprendre la suite du travail.

2.2.1 Les bases de la programmation fonctionnelle

Lisp est un langage orienté expression : il ne fait pas de distinction entre expres- sions et instructions comme le font de nombreux langages. Tout est expression et retourne une valeur ou un ensemble de valeurs. La plupart des expressions Lisp sont des applications de fonction. Ce que d’autres langages écrivent f(a,b,c), Lisp l’écrit (f a b c). Ainsi une somme ne se note pas 1+2+3+4 ni somme(1,2,3,4) mais (+ 1 2 3 4). Nous utilisons la même notation préfixée (dite, en arithmétique, notation polonaise) pour les formes spéciales et les macros : le premier élément dans la liste, dans ces cas, détermine comment les éléments suivants seront traités. Une expres- sion peut être une application de fonction, une forme spéciale ou une application de macro suivant la nature du premier élément.

Le langage Lisp peut être interprété ou compilé ; nous utiliserons surtout l’in- terprète. L’interaction entre l’interprète et l’utilisateur est très simple ; c’est une séquence de quatre étapes, répétée aussi longtemps que l’utilisateur le souhaite :

– l’utilisateur introduit une expression, c’est-à-dire un texte respectant certaines règles ;

– le système lit l’expression ; – le système évalue l’expression ;

– le système affiche la valeur de l’expression (ou un message approprié si l’ex- pression n’a pas de valeur).

Le rôle essentiel de l’interprète consiste donc à évaluer les expressions qui lui sont soumises, c’est-à-dire à en calculer la valeur.

2.2.2 Les expressions

On distingue trois sortes d’expressions : les expressions simples, les combinaisons et les formes spéciales.

(24)

Une expression simple est une constante (numérique ou non) ou une variable.

Signalons déjà que la valeur d’une constante est cette constante, tandis que la valeur d’une variable peut être un objet quelconque.

Une combinaison est une liste ; nous appellerons le premier élément opérateur ; les autres éléments sont les opérandes. Les éléments d’une combinaison sont eux-mêmes des expressions (expressions simples, combinaisons, formes spéciales) ; la valeur du premier élément doit être une fonction, les valeurs des suivants étant alors des ar- guments pour cette fonction.

Une forme spéciale est une liste dont le premier élément est un mot-clef spécifique ; le nombre et la nature des autres éléments varient selon le type de la forme spéciale.

Syntaxiquement, les expressions simples sont des (représentations de) nombres ou des identificateurs alphanumériques (les mots-clefs sont interdits, de même que certains caractères, les parenthèses notamment). Les formes, c’est-à-dire les com- binaisons et les formes spéciales, sont des listes. Avant d’introduire des définitions plus précises concernant notamment quelques formes spéciales importantes, nous donnons quelques exemples d’expressions simples et de combinaisons.

Exemple d’expressions simples

> 3 3

> 8/5 1 3/5

> -3.14 -3.14

> +

#<procedure:+>

> pi

3.141592653589793

> x

reference to an identifier before its definition: x

Les six évaluations ci-dessus concernent des expressions simples. Les constantes (ici, des nombres) s’évaluent en elles-mêmes. Un identificateur est vu par l’interprète comme une variable à laquelle une valeur est liée. L’interprète renvoie cette valeur, si elle existe ; sinon, un message signale “reference to an identifier before its definition”

(variable non liée à une valeur). Le symbole + est pré-lié (lié par le système) à la fonction d’addition ; les fonctions ne sont pas affichables explicitement, mais l’inter- prète signale que la valeur de “+” est une fonction. Enfin, nous avons supposé ici que la variable pi avait été liée précédemment à la valeur numérique 3.141592653589793, tandis que la variable x n’est pas liée.

(25)

Exemple de combinaisons

> (+ 2 5) 7

> (+ (* 3 -2) 2) -4

> (/ 2 0)

/: division by zero

> (/ 8 3.0)

2.6666666666666665

Les combinaisons évaluées ci-dessus sont des formes arithmétiques. Pour évaluer l’expression (+ 2 5), nous évaluons d’abord les trois sous-expressions +, 2 et 5, puis nous appliquons la première valeur (la fonction d’addition) aux deux suivantes (les valeurs 2 et 5), ce qui donne la valeur de l’expression, soit 7.

Le langage Lisp dispose d’une syntaxe très simple et élégante, utilisant un mini- mum de concepts. L’essentiel du langage Lisp est défini par seulement trois règles EBNF :

expression → atom | list

atom → number | name | string | operator list →(expression*)

Ces règles peuvent se traduire de la manière suivante en français : un programme Lisp est une liste d’expressions, chacune d’entre elles pouvant être une liste (récur- sivité) ou un atome.

2.2.3 La forme spéciale define

La forme spéciale de mot-clefdefine est utilisée pour établir une liaison entre une variable et une valeur. L’évaluation de la forme spéciale (define symb e) ne produit pas de valeur mais a pour effet la liaison de la valeur de l’expression e à la variable symb.

Exemple d’utilisation de define

> x

reference to an identifier before its definition: x

> (+ x 2)

reference to an identifier before its definition: x

> (define x 10)

> x

(26)

10

> (+ x 2) 12

2.2.4 Les symboles et leur double statut

Les textes que l’on peut introduire au clavier et soumettre au système sont com- posés de mots (au sens large), séparés les uns des autres par des caractères spéciaux tels l’espacement, le saut à la ligne, les parenthèses, etc. Certains de ces mots ont un rôle particulier, notamment les mots-clefs, tels que define et if, les constantes numériques (2, -4/3, 3.5-2.3i, 6.02e23, etc.), les constantes booléennes #t et#f. La plupart des autres mots tels x, y, somme, total, l sont des symboles. Les symboles peuvent avoir deux rôles bien distincts. Tout d’abord, ils peuvent être des constantes lexicales (par opposition à d’autres types de constantes comme les nombres et les valeurs booléennes). Ces constantes lexicales n’ont pour le système aucune signifi- cation particulière, mais elles ont généralement une signification pour l’utilisateur.

Par défaut cependant, le système n’attribue pas à un symbole le statut de constante lexicale, mais celui de variable. Comme nous l’avons vu au paragraphe précédent, la signification d’une variable, ou plus exactement sa valeur, est l’objet qui lui est lié, s’il existe. Pour que le système attribue à un symbole le statut de constante lexicale, nous utilisons le caractère spécial’(lire quote) : la valeur d’une expression constituée de ce caractère suivi immédiatement d’un symbole est ce symbole.

Exemple de symboles

> ’x

’x

> ’x + 2

’x

#<procedure:+>

2

> ’somme

’somme

2.2.5 Les paires et les listes

Les paires et listes en Lisp correspond aux définitions de couple et d’ensemble en mathématiques.

Les paires

(27)

Une paire est une liaison entre deux éléments : ( a . b )

cons est l’opérateur qui permet de constituer la paire ; car permet d’extraire a;

cdr permet d’extraire b.

Exemples de paires

> (cons 1 2) (1 . 2)

> (cons (cons 1 2) 3) ((1 . 2) . 3)

> (car (cons 1 2)) 1

> (cdr (cons 1 2)) 2

> (pair? (cons 1 2))

#t

(28)

Les listes

Une liste est soit la liste vide ((), null ou empty), soit une paire dont le car est un élément et le cdr une liste.

cons oulist sont des opérateurs permettant de constituer une liste, car permet d’extraire le premier élément de la liste ,

cdr permet d’extraire le second.

Remarque

Lorsqu’une paire est une liste c’est-à-dire quand son second élément est une liste alors le point qui sépare les deux éléments devient un espace.

Exemples de listes

> null ()

> (cons 3 null) (3)

> (define P ’(P0 P1 P2))

> P

’(P0 P1 P2)

> (car P)

’P0

> (cdr P)

’(P1 P2)

> (define T ’(T0 T1 T2))

> T

’(T0 T1 T2)

> (cons P T)

’((P0 P1 P2) T0 T1 T2)

> (cons P (list T))

’((P0 P1 P2) (T0 T1 T2))

> (define PT (cons l (list T)))

> PT

’((P0 P1 P2) (T0 T1 T2))

> (car PT)

’(P0 P1 P2)

> (cdr PT)

’((T0 T1 T2))

> (cadr PT)

’(T0 T1 T2)

(29)

Quelques fonctions de base sur les listes lenght

Retourne le nombre d’éléments de la liste list-ref

Retourne l’élémént d’une liste à une position passée en argument append

Construit une nouvelle liste à partir de deux listes existantes reverse

Renverse les éléments d’une liste member

Teste si un élément appartient à la liste equal ?

Permet de comparer deux listes

Exemple avec les fonctions de base sur les listes

> (append P T)

’(P0 P1 P2 T0 T1 T2)

> (length PT) 2

> (list-ref P 0)

’P0

> (reverse PT)

’((T0 T1 T2) (P0 P1 P2))

> (member ’P1 P)

’(P1 P2)

> P

’(P0 P1 P2)

> (member ’P4 P)

#f

> (equal? ’(p1 p2) ’(p1 p2))

#t

> (eq? ’(p1 p2) ’(p1 p2))

#f

2.2.6 Les fonctions

Les fonctions sont des types de données qui prennent en entrée des paramètres et retournent une valeur, un symbole ou une liste. Par exemple, il est possible d’écrire

(30)

des fonctions qui prennent d’autres fonctions en paramètre, et retournent des fonc- tions.

Syntaxe

(define (f1 a b c d f2) ... Corps de la fonction...)

Exemple

print affiche les éléments d’une liste donnée en paramètre

>(define (print E) (for ([elt E])

(printf "~a " elt)))

>(print P) (P1 P2 P3)

La fonctionlambdaest une fonction anonyme soit à usage local ou à usage unique.

Elle apparait dans le corps d’une fonction et sa définition est alors valable pour le corps de la fonction. En mode interprété, une définition la rend à usage unique. A noter que l’on peut faire en même temps de l’initialisation avec la fonction lambda.

C’est le cas avec l’exemple ((lambda (x [y 5]) (list y x)) 1 2) où la variable y est initialisée à 5. Si une nouvelle variable est spécifiée en paramètre, elle est utilisée pour l’évaluation de la fonction, sinon le cinq (5) est utilisé par défaut.

Exemples Exemple 1:

> ((lambda (x) x) 10) 10

> ((lambda (x y) (list y x)) 1 2) (2 1)

> ((lambda (x [y 5]) (list y x)) 1 2) (2 1)

> ((lambda (x [y 5]) (list y x)) 1) (5 1)

Exemple 2:

> (twice (lambda (s) (string-append s "!")) "hello")

"hello!!"

(31)

2.2.7 Les structures conditionnelles

La syntaxe globale des structures conditionnelles se présente comme suit : Syntaxe

( if expr expr expr ) ( and expr * )

( or expr * )

( cond {[ expr expr * ]} * ) La clause IF

IF permet de tester une condition et d’afficher une valeur si la condition est vérifiée et une autre valeur sinon.

Exemple

> (if (> 2 3)

"plus grand"

"plus petit")

"plus petit"

La clause AND

AN D permet de mettre plusieurs conditions ensemble. Si l’une est fausse, le ré- sultat du test est #f.

Exemple

(define (sup a b)

(if (and (number? a) (number? b)) (if (< a b) (display "plus grand")

(display "plus petit"))(display "pas comparable")))

> (sup 2 5) plus grand

> (sup 2 "toto") pas comparable La clause COND

CON D permet de faire un choix selon un critère. Il nous permet de nous affran- chir de plusieursIF.

Syntaxe

(32)

(cond [... ] [... ]

[else ...] ) Exemple:

(define P (list P0 P1 P2)) (define T (list t0 t1 t2)) (define P1 empty)

(define T1 (list t0 t1 t2)) (define (analyseRdP P T)

(cond

[(and (empty? P) (empty? T)) (print "Rdp vide") ] [(or (empty? P) (empty? T)) (print "Rdp dégénéré")]

[else (printf

"il y a ~a places et ~a transitions"

(length P) (length T))])) Test

> (analyseRdP P T)

il y a 3 places et 3 transitions

> (analyseRdP P1 T1)

"Rdp dégénéré"

2.2.8 Fonction Récursive

La récursivité est une notion essentielle au cœur de la programmation fonction- nelle. Les fonctions récursives sont les fonctions qui peuvent s’auto-appeler.

Exemple 1

membre teste si un élément est dans une liste (define (membre elt E)

(cond

[(empty? E) #f]

[(equal? (car E) elt) #t]

[else (membre elt (cdr E))]))

(33)

Exemple 2

membre2 cherche à montrer qu’un élément appartient éventuellement à une sous- liste.

(define (membre2 elt E) (cond

[(empty? E) #f]

[(equal? (car E) elt) #t]

[(list? (car E))

(or (membre2 elt (car E)) (membre2 elt (cdr E)))]

[else (membre2 elt (cdr E))]))

2.2.9 Itérateur for

L’itérateur for permet de parcourir une liste en Racket et permet aussi de s’arrê- ter lorsque certaines conditions sont respectées.

Itérateur for/or Syntaxe :

(for/or (for-clause ...) body ...+)

Lorsque la dernière expression de body produit #t alors l’itération s’arrête et le résultat est #t. Si le body n’est jamais évalué alors le résultat est #t sinon le résultat est #f.

Exemples :

> (for/or ([i ’(1 2 3 "x")]) (print i) (i . < . 3)) 1#t

> (for/or ([i ’(1 2 3 4)]) i)

1

> (for/or ([i ’()])

(error "pas de parcours de liste"))

#t

Remarque : le corps n’a pas été évalué et #t est retourné

(34)

(define (Pre t) (cond

[(empty? ˚T) empty]

[else (for/or ([pre ˚T])

(and (equal? t (car pre)) (cdr pre)))])) (define (Post t)

(cond

[(empty? T˚) empty]

[else (for/or ([post T˚]) (and (equal? t (car post))

(cdr post)))]))

Les deux dernières fonctions recherchent respectivement les prédécesseurs et les successeurs d’une transition.

Itérateur for/and Syntaxe

(for/and (for-clause ...) body ...+)

Lorsque la dernière expression de body produit#f, l’itération s’arrête, le résultat de l’évaluation du for/and est#f. Si le body n’est jamais évalué alors le résultat est

#t. Sinon le résultat est l’évaluation du dernier élément.

Exemple

> (for/and ([i ’(1 2 3 "x")]) (i . < . 3))

#f

> (for/and ([i ’(1 2 3 4)]) i)

4

> (for/and ([i ’()])

(error "pas de parcours de liste"))

#t

Remarque : le corps n’a pas été évalué et #t est retourné Clause When

(35)

La clause W hen nous permet de poser certaines conditions sur les itérations et d’évaluer seulement certaines expressions.

Syntaxe

(for (for-clause ...) body ...+) for-clause = [id seq-expr]

[(id ...) seq-expr]

#:when guard-expr

#:unless guard-expr

Si une clause est de type # : when guard-expr, alors seules les clauses qui pré- cèdent déterminent l’itération et body est effectivement évalué selon la forme sui- vante :

(when guard-expr

(for (for-clause ...) body ...+)) 2.2.10 Les vecteurs

Un vecteur est une fonction, au sens mathématique, d’un ensemble index dans le domaine des valeurs licites du langage. L’ensemble index est un sous-ensemble fini d’entier. Plus formellement, les vecteurs en Racket sont des structures de données hétérogènes dont les éléments sont indexés par des entiers consécutifs commençant par 0. A chaque valeur d’indice nous faisons correspondre l’élément correspondant.

La longueur d’un vecteur est le nombre de ses éléments.

Exemple d’un vecteur

#(4 (2 5 9 12) "an")

L’exemple précédent représente un vecteur en Racket de longueur 3 dont l’élé- ment d’indice 0 est 4, celui d’indice 1 est (2 5 9 12) et celui d’indice 2 est "an".

Quelques procédures sur les vecteurs (make-vector k [init])

Crée un vecteur de k éléments initialisés à init (optionnel) (vector ? obj)

Renvoie #t si obj est un vecteur et #f autrement (vector-lenght vecteur)

Retourne le nombre d’éléments d’un vecteur

(36)

(vector-ref vecteur k)

Retourne la valeur de l’élément k d’un vecteur (vector-set ! vecteur k objet)

Modifie un élément quelconque, de rang k, d’un vecteur (vector → list vecteur)

Retourne une liste constituée par les éléments d’un vecteur (list → vector liste)

Retourne un vecteur initialisé avec les éléments de liste 2.2.11 Les tables de hashing

On a vu précédemment les listes. La recherche d’éléments dans une liste devient difficile dès que le nombre d’éléments dans la liste devient important. DrRacket dis- pose d’une autre structure de données qui associe à chaque valeur une clé : les tables de hashing. Les tables de hashing font donc une correspondance entre une clé et une valeur (à une seule clé correspond une seule valeur), ce qui facilite la recherche d’éléments. Les tables de hashing seront très utilisées dans la suite du travail surtout dans la partie du dépliage pour représenter notamment l’ensemble des conditions et des évènements.

Quelques fonctions utiles des tables de hashing (make-hash)

Crée une table de hashing vide (hash ? v)

Retourne #t si v est une table de hashing et #f sinon (hash-set ! hash key v)

Insère une clé et une valeur dans la table (hash-ref hash key )

Retourne la valeur correspondante à cette clé (hash-has-key ? hash key)

vérifie si une certaine clé existe dans la table (hash-update ! hash key updater

Modifie la valeur associée à une clé (hash-remove ! hash key)

Supprime la clé et la valeur correspondante dans la table (hash-keys hash)

Retourne une liste des clés de la table (hash-values hash)

Retourne une liste des valeurs de la table (hash->list hash)

(37)

Retourne une liste des éléments de la table (hash-count hash)

Retourne le nombre d’éléments de la table

Conclusion partielle

Dans ce chapitre, il est question de la programmation fonctionnelle. Lisp de par sa structure basée sur les fonctions récursives, des structures de données assez simples (paires, listes, vecteurs, tables de haching), des fonctions de parcours de listes permet de mettre en place des codes naturels très proches des définitions mathématiques.

(38)

Chapitre 3

Travaux réalisés

De la modélisation des systèmes temps réels, notamment par les réseaux de Petri, nous présenterons dans ce chapitre des fonctions, méthodes et librairies pour réaliser une implémentation sous DrRacket. D’entrée, nous présenterons le matériel utilisé et l’architecture logicielle pouvant aboutir à la réalisation du dépliage des RdP. Ensuite nous aborderons la méthode de parsing adoptée et présenterons les modélisations mathématiques et leurs implémentations avec DrRacket. Le dépliage des RdP sera abordé en dernier point.

3.1 Matériel

3.1.1 L’outil Romeo pour l’édition des RdP

Le logiciel Romeo1 [GLMR05, LRST09] est développé à l’IRCCyN2 (Institut de Recherche en Communications et Cybernétique de Nantes) au sein de l’équipe

“Systèmes Temps Réel”. Il est composé d’une interface graphique (écrite en Tcl/Tk) permettant l’édition et la simulation de réseaux de Petri temporels et à chrono- mètres, éventuellement paramétrés, et d’un module de calcul MERCUTIO (écrit en C++) qui réalise des analyses d’espaces d’états et du model-checking. Il est distri- bué librement sous la licence CeCILL et est disponible pour les plateformes Win- dows, MacOSX et Linux. L’interface graphique propose le choix entre trois modes temporels suivant l’extension désirée : les TPN (Réseaux de Petri Temporels), et deux extensions à chronomètres : les réseaux de Petri étendus à l’ordonnancement (Scheduling-TPN) [BFSV04, RD02]. Pour chacune de ces extensions, Romeo propose une extension paramétrée correspondante (PTPN, Scheduling-PTPN ou PITPN).

L’interface graphique fournit un simulateur interactif pour tester des scénarios. Il permet d’étudier une trace particulière dans l’espace d’états du modèle afin de détec- ter en première analyse des erreurs de modélisation. Plusieurs méthodes d’explora- tion de l’espace d’états sont pour cela disponibles : le graphe de classes d’états pour

1. Disponible à l’adresse http ://romeo.rts-software.org/

2. www.irccyn.fr

Références

Documents relatifs

Dans les différentes phases correspondant à la gestion d’un réseau de transport public, nous situons nos travaux au stade de la planification, avec pour objectif

Premièrement, j’ai fait une recherche bibliographique et cyberographique dans le but de construire un cadre théorique évoquant les concepts suivants : le genre, l’identité

La valorisation de la recherche à l’UPJV est également mise en exergue par des acteurs de transfert tels que la Société d’Accélération de Transfert de Technologie

La sémantique d’un réseau de Petri A-temporisé est la même que celle d’un réseau de Petri ordinaire à ceci près qu’un jeton ne peut franchir une transition t à partir

Pour introduire tous les mots-cl´es en une phrase, nous dirons que notre th`ese va s’int´eresser ` a la sp´ecification et ` a la v´erification des syst`emes de tˆ aches temps r´eel

La durée moyenne de cette étude a été de 12 mois durant laquelle on a étudié la variation des paramètres biochimiques et hormonaux au cours de la gestation et durant la période

Ce qui est intéressant pour l’orateur, c’est qu’il y a deux perspectives dans le développement des nanotechnologies et leur impact politique : la première consiste à

Ainsi, si le jeu de données de bio-logging global analysé dans le cadre de cette thèse (i.e. toutes années confondues), relatif aux femelles éléphants de mer de l’archipel