Télécharger la présentation
La présentation est en train de télécharger. S'il vous plaît, attendez
Publié parAnge Lhomme Modifié depuis plus de 9 années
1
Packages et Types De la Spécification Formelle A l'implémentation Ada
2
Les Comptes (sans historique) Le seul constructeur est Open. Chaque terme de la syntaxe tel que Put (Get (Put (Open (m), n), p), q) peut donc se réduire à un terme équivalent Open (m+n-p+q) Dès lors qu'il est sémantiquement correct : p <= m + n
3
Les Comptes (σ-algèbre des termes) Un compte peut donc être représenté par le terme réduit formé - de l'identifiant du constructeur qui l'a produit, - des arguments fournis à ce constructeur.
4
Les Comptes (constructeurs) Les constructeurs d'un type sont toujours en nombre fini. Leurs identifiants peuvent donc être représentés par une énumération : Type Builder is (Open);
5
Les Comptes (type) Le terme peut avoir des arguments différents selon le constructeur sur lequel il repose. On peut donc le représenter par un enregistrement avec variante. Type Account (selon : Builder) is record case … end case ; end record;
6
Les Comptes (type) Le terme peut avoir des arguments différents selon le constructeur sur lequel il repose. On peut donc le représenter par un enregistrement avec variante. Type Account (selon : Builder) is record case … end case ; end record;
7
Chaque variante comportera des champs correspondant à ses arguments. Les Comptes (type) Type Account (selon : Builder) is record case selon is when Open => balance : natural ; end case ; end record; La taille de l'enregistrement n'est pas déterminée tant que le constructeur ne l'est pas (il est non contraint)
8
Cette représentation interne du compte par son terme représentatif ne concerne pas l'utilisateur : elle est en zone private Les Comptes (terme) private type Builder is (Open); type Account (selon : Builder) is record case selon is when Open => balance : natural ; end case; end record;
9
L'utilisateur doit avoir accès (public) : - au type (non contraint), - aux opérations sur ce type. Les Comptes (opérations) type account (<>) is private ; function Open (m : natural) return account; function Put (a : account ; m : natural) return account; function Get (a : account ; m : natural) return account; function Balance (a : account ) return natural; Le type privé est muni d'une égalité implicite qui n'a pas été axiomatisée...
10
Les types et les profils des opérations de la sorte forment la spécification d'un package, avec deux parties : publique et privée. Les Comptes (spécification) package Accounts is type account (<>) is private; function Open (m : natural) return account; function Put (a : account ; m : natural) return account; function Get (a : account ; m : natural) return account; function Balance (a : account) return natural; private type builder is (Open); type account (selon : builder) is record case selon is when Open => balance:natural; end case; end record ; end accounts;
11
Le corps du package : - alloue les termes construits, - définit observateurs et extenseurs (conformément aux axiomes), - assure la vérification des préconditions (par des assertions) Les Comptes (corps) package body Accounts is... end Accounts;
12
L'allocation du constructeur. Les Comptes (constructeur) package body Accounts is function Open (m : natural) return account is begin return (selon => Open, balance => m) ; end Open ;... end Accounts;
13
Observateurs et extenseurs sont définis selon les axiomes Les Comptes (extenseurs/observateurs) package body Accounts is... function Balance (a : account) return natural is begin return a.balance ; end Balance; function Put (a : account ; m : natural) return account is begin return Open (a.balance + m) ; end Put;... end Accounts;
14
les préconditions (traduites par des assertions) Les Comptes (assertions) package body Accounts is... function Get (a : account ; m : natural) return account pragma assert (m <= balance (a)); is begin return Open (a.balance - m) ; end Get ; end Accounts; L'assertion n'est effective que si le package est compilé avec l'option ad'hoc (-gnata).
15
Dans cette mouture, - outre le solde - l'objectif est d'accéder à l'historique des opérations. il y a plusieurs constructeurs : - l'un initialise le compte (Open) - les deux autres (Get et Put) utilisent un terme construit (c). Les Comptes avec Historique -- Constructeurs open (m) => ACCOUNT; put (c, n) => ACCOUNT; get (c, n) => ACCOUNT :: n <=balance(c) ; :: n <=balance(c) ; -- Observateurs balance (c) => NATURAL is balance (open (m)) → m | balance (put (c, n)) → balance (c)+n | balance (get (c, n)) → balance (c)-n; operations (c) => NATURAL is operations (open (m)) → 0 | operations (put (c, n)) → operations (c)+1 | operations (get (c, n)) → operations (c)+1; history (c, i) => NATURAL :: i NATURAL :: i <=operations (c) is history (open (m), i) → m | history (put (c, n), i) → if operations (c) + 1 <= i then history (c, i-1)+n else history (c, i) else history (c, i) | history (get (c, n), i) → if operations (c) + 1 <= i then history (c, i-1)-n else history (c, i); else history (c, i); end accounts; sort accounts is -- Types Déclarés ACCOUNT ; -- Variables c : ACCOUNT ; i, m, n: NATURAL; end Accounts; La représentation du terme sera donc récursive.
16
Le compte représenté par un pointeur : - devient un type contraint, - est limité pour éviter une sémantique douteuse de l'égalité, le partage de pointeurs. Les Comptes (spécification) type account is limited private; function Open (m : natural) return account; function Put (a : account ; m : natural) return account; function Get (a : account ; m : natural) return account; function Balance (a : account ) return natural; function Operation (a : account) return natural; function history (a : account ; n : natural) return natural;
17
Les Comptes (spécification) package Accounts is type account is limited private ; function Open (m : natural) return account; function Put (a : account ; m : natural) return account; function Get (a : account ; m : natural) return account; function Balance (a : account) return natural; function Operations (a : account) return natural; function history (a : account ; n : natural) return natural; private type builder is (Open, Put, Get); type term (selon : builder) is record case selon is when Open => initial : natural ; when Put | Get => changed : account ; change : natural ; end case; end record ; type account is access term; end Accounts;
18
Un constructeur alloue dans le « tas » un terme dont l'adresse représente un compte. Les Comptes (constructeurs) function Open (m : natural) return account is begin return new Term'(Selon => Open, Initial => m); end ; function Put (a : account; m : natural) return account is begin return new Term'(Selon => Put,Changed => a, Change => m); end Put ; function Get (a : account; m : natural) return account is pragma Assert (m <= balance (A)); begin return new Term'(Selon=>Get, Changed=>a, Change=>m); end Get;
19
Un observateur rend compte cas par cas des axiomes rédigés pour chaque constructeur. a.all.changed n'est rien d'autre que l'état du compte tel qu'il était avant qu'on lui applique le dernier Get|Put. La récursivité se construit sur la trame fixée par les variantes du type term. Les Comptes (observateurs) function Operations (a : account) return natural is begin case a.all.Selon is when Open => return 0; when Get | Put => return Operations (a.all.changed) + 1; end case; end Operations;
20
Des opérations de même profil (Get|Put) peuvent engendrer, dans le respect des axiomes, des traitements différents au sein d'un même observateur. Les Comptes (observateurs) function Balance (A : Account) return Natural is begin case A.all.Selon is when Open=>return A.all.Initial; when Put=>return Balance (A.all.Changed) + A.all.Change; when Get=>return Balance (A.all.Changed) - A.all.Change; end case; end Balance ;
21
History se définit récursivement. Chaque appel utilise Operations, récursive. quadratique La complexité est quadratique... Les Comptes (observateurs) function history (A : Account; N : Natural) return Natural is pragma Assert (N <= Operations (A)); begin case A.all.Selon is when Open => return A.all.Initial; when Put => if Operations (A) <= N then return history (A.all.Changed, N - 1) + A.all.Change; else return history (A.all.Changed, N); end if; when Get => if Operations (A) <= N then return history (A.all.Changed, N - 1) - A.all.Change; else return history (A.all.Changed, N); end if; end case; end history ;
22
Pour retrouver les « facilités » de l'affectation, on renonce à l'attribut limited. Il faut alors définir une égalité sémantique sur les comptes (même historique) En utilisant : L'égalité des entiers = L'égalité récursive = Les Comptes (avec égalité) x, y : account ; m, n : natural ; _=_ (x,y)=>boolean is Open (m)= Open(n) → m=n Open (m)= Open(n) → m=n | Open (m)= Get(y,n) → false | Open (m)= Put(y,n) → false | Get(x,m) = Open(n) → false | Get(x,m) = Get(y,n) → x=y and m=n | Get(x,m) = Put(y,n) → false | Put(x,m) = Open(n) → false | Put(x,m) = Get(y,n) → false | Put(x,m) = Put(y,n) → x=y and m=n; ᄇ
23
Elle se tratuit immédiatement par l'observateur Les Comptes (avec égalité) function "=" (X, Y : Account) return Boolean is begin case X.all.Selon is when Open => case Y.all.Selon is when Open => return X.all.Initial = Y.all.Initial; when Put | Get => return false ; end case ; when Put => case Y.all.Selon is when Open | Get => return false ; when Put => return x.all.changed = y.all.changed and then x.all.change = y.all.change ; end case ; when Get => case Y.all.Selon is when Open | Put => return false ; when Get => return x.all.changed = y.all.changed and then x.all.change = y.all.change ; end case; end case; end "=";
24
L'implémentation standard par les sigma termes - engendre des observateurs de complexité au moins linéaire - nécessite une utilisation des pointeurs pour traduire la structure récursive, - demande une définition explicite de l'égalité. On cherche une implémentation dans laquelle - les opérations se font en temps constant, - les pointeurs brillent par leur absence, - l'égalité est native. Les Comptes (σ-termes)
25
En utilisant un tableau - on a tout gagné (y compris l'égalité par défaut) - mais le type est non contraint, Les Comptes (tableau) package Accounts is type account (<>) is private ; function Open (m : natural) return account; function Put (a : account ; m : natural) return account; function Get (a : account ; m : natural) return account; function Balance (a : account) return natural; function Operations (a : account) return natural; function History (a : account ; n : natural) return natural; private type naturals is array (natural range <>) of natural ; type account (Operations : natural) is record history : naturals (0.. Operations) ; end record; end Accounts;
26
Les Comptes (tableau : constructeurs) package body Accounts is – les constructeurs function Open (m : natural) return account is begin return (operations => 0, history => (0 => m)) ; end; function Put (a : account ; m : natural) return account is begin return (operations => a.operations+1, history => a.history & (a.history(a.operations) +m)) ; end Put; function Get (a : account ; m : natural) return account is pragma assert (m <= Balance (a)); begin return (operations => a.operations+1, history => a.history & (a.history(a.operations) -m)) ; end Get;... end Accounts;
27
Les observateurs sont - soit un accès direct à un champ d'enregistrement, - soit l'accès (direct) à un élément de tableau, - soit une combinaison des deux. Les Comptes (tableau : observateurs) package body Accounts is... – les observateurs function Balance (a : account) return natural is begin return a.history (a.operations); end ; function Operations (a : account) return natural is begin return a.operations; end Operations; function History(a : account ; n : natural) return natural is pragma assert (n <= Operations (a)) ; begin return a.history(n); end history; end Accounts;
28
- la spécification algébrique peut se traduire mot à mot en ADA en utilisant un terme constitué de constructeurs comme représentation des objets. - cette traduction implique des pointeurs dès lors que la structure de donnée est récursive. - elle n'interdit pas d'autres implémentations plus efficaces qui partageront la partie publique de sa spécification ADA, ainsi que les préconditions et dont il faudra prouver qu'elles respectent les axiomes. De la Spécification Formelle A l’Implémentation Ada
29
Les types abstraits d'Ada vont permettre de déclarer dans un package représentant la sorte - les types de cette sorte sans les implémenter, - les profils des opérations sans les définir. Implémentation Ada types abstraits
30
L'attribut abstract du type account permet de déclarer les opérations dans les définir. Pour pouvoir créer des fils concrets du type abstrait, il faut un type extensible (tagged). Ces fils concrets seront dans l'obligation de définir les opérations surchargeant les abstraites. L'égalité doit permettre de comparer un compte à un autre, même si ce dernier n'a pas le même type concret ; donc un seul argument est abstrait, les autres sont pris dans la classe du type abstrait (account'class ). La définition du type est privée (private) et vide (null record). Les Comptes (abstraction) package Accounts is type Account is abstract tagged private; function Open (M : Natural) return account is abstract; function Put (A : account ; M : Natural) return account is abstract; function Get (A : account ; M : Natural) return account is abstract; function Balance (A : account) return Natural is abstract; function Operations (A : account) return Natural is abstract; function History (A : account ; N : Natural) return Natural is abstract; function"="(X : account; Y : account'class) return Boolean is abstract; private type Account is abstract tagged null record; end Accounts; ²
31
les développements - de l'application sur la classe : procedure manage (x : in out accounts.account'class); - des implémentations de la classe type account is new accounts.account with private; sont indépendants, s'appuient sur le type abstrait. Implémentation Ada classes
32
les développements - de l'application sur la classe : procedure manage (x : in out accounts.account'class); - des implémentations de la classe type account is new accounts.account with private; sont indépendants, s'appuient sur le type abstrait. Implémentation Ada classes
33
Les Comptes (application) Cette application sera utilisable sur toute implémentation du pourvu qu'elle appartienne à accounts.account'class. with accounts, ada.text_io; use accounts, ada.text_io; procedure manage (a : in out accounts.account'class) is c : character; begin put ("initial?"); a:=open(natural'value(get_line)); loop begin put ("balance=" & Natural'Image (Balance (A))); put (" [P]ut |[G]et |[A]mount |[Q]uit?"); Get (C); case c is when 'p' => a:= put(a, natural'value(get_line)); when 'g' => a:= get(a, natural'value(get_line)); when 'a' => put(natural'image (history(a,natural'value(get_line)))); when 'q' => skip_line; exit; when others => skip_line; end case; exception when others => Put ("erreur"); Skip_Line; end; end loop; end manage ;
34
Les Comptes (σ-implémentation) Le type accounts.sigma.account (concret) hérite de accounts.account (abstrait) Le premier doit donc définir les opérations abstraites du second. Le type concret devient un modèle. package accounts.sigma is type account is new accounts.account with private; function open (initial : natural) return account; function put (a : account; m : natural) return account; function get (a : account; m : natural) return account; function balance (a : account) return natural; function operations (a : account) return natural; function history (a : account;m : natural) return natural; function "="(x : account; y : accounts.account'class) return boolean; private type term (<>) ; type model is access term; type builder is (Open, Put, Get); type term (selon : builder) is record case selon is when Open => initial : natural ; when Put|Get=>changed:model; change:natural ; end case; end record ; type account is new accounts.account with record some : model; end record ; end accounts.sigma;
35
Les Comptes (implémentation optimale) Ce package ne se distingue du package accounts.optimal que par le modèle. Ce modèle doit être un type contraint : pour cela on utilise un pointeur sur la structure non contrainte. package accounts.optimal is type account is new accounts.account with private; function open (initial : natural) return account; function put (a : account; m : natural) return account; function get (a : account; m : natural) return account; function balance (a : account) return natural; function operations (a : account) return natural; function history (a : account;m : natural) return natural; function "="(x : account; y : accounts.account'class) return boolean; private type naturals is array (natural range <>) of Natural; type unconstrained (operations : Natural) is record history : naturals (0.. operations); end record; type model is access unconstrained; type account is new accounts.account with record some : model; end record ; end accounts.optimal;
36
Les Comptes (implémentation générique) Dans la spécification du package générique - la partie publique est celle de toutes les implémentations, - l'égalité est définie y compris entre implémentations différentes, - la définition du modèle n'a plus sa place en partie privée, il est générique. generic type model is private;... package accounts.implementation is type account is new accounts.account with private; function open (initial : natural) return account; function put (a : account; m : natural) return account; function get (a : account; m : natural) return account; function balance (a : account) return natural; function operations (a : account) return natural; function history (a : account;m : natural) return natural; function "="(x : account; y : accounts.account'class) return boolean; private type account is new accounts.account with record some : model; end record ; end accounts.implementation;
37
Les Comptes (implémentation générique) Dans le corps du package générique, seules restent les fonctions - traduisant le morphisme entre modèle et implémentation, - en exprimant les préconditions.... package body Accounts.Implementation is function Open (M : Natural) return Account is begin return (Accounts.Account with some => Open (M)); end; function Put (A : Account; M : Natural) return Account is begin return (Accounts.Account with some => Put (A.some, M)); end; function Get (A : Account; M : Natural) return Account is pragma Assert (M <= Balance (A)); begin return (Accounts.Account with some => Get (A.some,M)); end; function Operations (A : Account) return Natural is begin return Operations (A.some); end Operations; function History (A : Account; N : Natural) return Natural is pragma Assert (N <= Operations (A)); begin return History (A.some, N); end History;... end Accounts.Implementation ;
38
Les Comptes (implémentation générique) Les opérations pour lesquelles existe une opération de classe sont facultatives pour le modèle (pointeur null) ; on utilise alors par défaut la version de classe. Les opérations qui impliquent éventuellement des implémentations différentes nécessitent l'opération de classe. with Accounts.Class.Balance, with Accounts.Class.Equal; package body Accounts.Implementation is... function Balance (A : Account) return Natural is begin if Access_Balance /= null then return Access_Balance.all (A.some); else return Class.Balance (A); end if; end Balance; function "=" (X : Account; Y : Accounts.Account'Class) return Boolean is begin if not (Y in Account) then return Class.Equal(X, Y); elsif Access_Equal /= null then return Access_Equal.all (X.some, Account (Y).some); else return Class.Equal (X, Y); end if; end "="; end Accounts.Implementation ;
39
Les Comptes (implémentation générique) Les opérations sur les modèles sont des paramètres de généricité qui prennent deux formes : Si existe une fonction de classe, le paramètre générique prend la forme d'un pointeur (éventuellement null) sur le profil de l'opérateur. Sinon, il prend la forme d'un profil ; l'association à un opérateur effectif de même profil est automatique par la présence de <> ; generic type Model is private; with function Open (M : Natural) return Model is <>; with function Put (A : Model ; M : Natural) return Model is <>; with function Get (A : Model ; M : Natural) return Model is <>; with function Operations (A : Model) return Natural is <>; with function History (A : Model ; N : Natural) return Natural is <>; Access_Balance : access function (X : Model) return Natural := null; Access_Equal : access function (X, Y : Model) return Boolean := null; package Accounts.Implementation is … end Accounts.Implementation;
40
Les Comptes (classe) Les opérations de classe sont filles d'un package class qui permet de les préfixer. package Accounts.Class is end Accounts.Class; function Accounts.Class.Balance (X : Account'Class) return Natural; function Accounts.Class.Equal (X, Y : Account'Class) return Boolean;
41
Les Comptes (fonctions de classe) La première est une simple composition de fonctions. La seconde utilise une fonction récursive auxiliaire. function Accounts.Class.Balance (X : Account'Class) return Natural is begin return History (X, operations (X)); end Accounts.Class.Balance; function Accounts.Class.Equal (X, Y : Account'Class) return Boolean is function Same (N : Natural) return Boolean is begin if N = 0 then return History (X, 0) = History (Y, 0); else return History (X, N) = History (Y, N) and then Same (N - 1); end if; end Same; begin return Operations (X) = Operations (Y) and then Same (Operations (X)); end Accounts.Class.Equal;
Présentations similaires
© 2024 SlidePlayer.fr Inc.
All rights reserved.