Réinitisalisation (modulaire)
Bloc = Soit une liste de blocs, soit une liste d’équations soit la réinitialisation d’un bloc (entre let
et tel
)
Exemple de bloc de réinitialisation : reset bloc every expression
node nat_reset() returns (o : int)
var c : bool;
let
reset o = 0 fby (o + 1) every c;
c = true fby false fby false fby c;
tel
(* qui est équivalent à *)
node nat_reset2() returns (o : int)
var c : bool;
let
o = if c then 0 else (0 fby (0 + 1));
c = true fby false fby false fby c;
tel
(* exemple avec une fonction externe *)
node nat() returns (o : int)
let
o = 0 fby (o + 1);
tel
node nat_reset() returns (o : int)
var c : bool;
let
(* fonctionne avec une fonctionne *)
reset o = nat() every c;
c = true fby false fby false fby c;
tel
(* ça, ça ne fonctionne pas *)
node nat_reset2() returns (o : int)
var c : bool;
let
o = if c then 0 else nat();
c = true fby false fby false fby c;
tel
Reset = réinitialisation modulaire, passe à travers les abstractions
Utile, mais rend le programme rapidement incompréhensible.
Types structurés et énumérés
Types structurés
type cpl = { re : float; im : float }
node add(x, y: cpl) returns (o: cpl)
let
o = { re = x.re +. y.re
; im = x.im +. y.im };
tel
node conj(x: cpl) returns (o: cpl)
let
o = { re = x.re
; im = -.x.im };
tel
Mot-clé fun
permet de définir une node
qui ne dépend pas du temps (pas d’état). Donc, par exemple, fby
est rejeté des fun
.
type cpl = { re : float; im : float }
fun add(x, y: cpl) returns (o: cpl)
let
o = { re = x.re +. y.re
; im = x.im +. y.im };
tel
fun conj(x: cpl) returns (o: cpl)
let
o = { re = x.re
; im = -.x.im };
tel
Types énumérés
l | a | c | a | b | c | b | |
---|---|---|---|---|---|---|---|
accept | faux | vrai | faux | faux | vrai | faux | faux .. faux .. faux .. |
type alpha = A | B | C (* commence par une majuscule, alpha est soit A, B, ou C *)
type etats = X | Y | Z | Mort
node automate(l : alpha) returns (accept : bool)
var etat_courant, etat_precedent : etats;
let
etat_courant = if (etat_precedent, l) = (X, A) then Y
else if (etat_precedent, l) = (Y, B) then Z
else if (etat_precedent, l) = (Y, C) then Y
else if (etat_precedent, l) = (Z, A) then Y
else Mort;
etat_precedent = X fby etat_courant;
accept = (etat_courant = Z);
tel
Horloges
Exercice : Définir une suite o : int telle que o_{2k} = k o_{2k+1}=0
o = 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, \dots
node k() returns (o: int)
var p: bool; v: int;
let
o = if p then v else 0;
v = 0 fby if p then v else (v + 1);
p = true fby not p;
tel
On remarque que o c’est la suite naturelle séparée de 0.
On peut donc mélanger/entrelacés la suite des entiers naturels et la suite des 0.
Opérateur d’entrelacement merge
node nat() returns (o : int)
let
o = 0 fby (o + 1);
tel
node k() returns (o: int)
var p: bool; v: int;
let
o = merge p v 0;
v = nat();
p = true fby not p;
tel
Contrairement au if
qui va perdre des données, le mélange entrelace les flux alors que le if
/else
choisis quelle valeur garder à un instant t.
0 | 1 | 2 | 3 | 4 | 5 | … | |
---|---|---|---|---|---|---|---|
v |
0 | abs | 1 | abs | 2 | abs | … |
p |
true |
false |
true |
false |
true |
false |
… |
o |
0 | 0 | 1 | 0 | 2 | 0 | … |
Le merge ne récupère que les informations dont il a besoin pour éviter de faire grossir la mémoire. |
Dans notre cas, p
est notre horloge, p
permet d’alterner le calcul entre p
et v
.
Sémantique
\begin{aligned} & \lb \text{merge } c e_{1} e_{2} \rb \\ &\begin{cases} \lb e_{1} \rb_{k} & \text{si } \lb c \rb_{k} = \text{ true et } \lb e_{1} \rb \neq \text{abs}, \lb e_{2} \rb_{k} = \text{abs} \\ \lb e_{2} \rb_{k} & \text{si } \lb c \rb_{k} = \text{ false et } \lb e_{1} \rb = \text{abs}, \lb e_{2} \rb_{k} \neq \text{abs} \\ \text{abs} & \text{si } \lb c \rb_{k} = \text{ abs et } \lb e_{1} \rb = \lb e_{2} \rb_{k} = \text{abs} \end{cases} \end{aligned}
Opérateur de sélection (échantillonnage) e
when
c
0 | 1 | 2 | 3 | … | |
---|---|---|---|---|---|
nat() |
0 | 1 | 2 | 3 | … |
nat() when p |
0 | abs | 2 | abs | … |
Sémantique
\begin{aligned} & \lb e \text{ when } c \rb \\ &\begin{cases} \lb e \rb_{k} & \text{si } \lb c \rb_{k} = \text{ true et } \lb e \rb_{k} \neq \text{abs} \\ \text{abs} & \text{si } \lb c \rb_{k} = \text{ false et } \lb c \rb_{k} = \text{abs} \end{cases} \end{aligned}
\lb c \rb_{k} | \lb e \rb_{k} | \lb e \text{ when } c \rb_{k} |
---|---|---|
true |
\neq abs | \lb e \rb_{k} |
false |
\neq abs | abs |
abs | abs | abs |
En contraste, voici le tableau pour les additions
\lb e_{1} \rb_{k} \lb e_{2} \rb_{k} \lb e_{1} + e_{2} \rb_{k} \neq abs \neq abs \lb e_{1} \rb_{k} + \lb e_{2} \rb_{k} abs abs abs
Si on a une sortie avec une horloge spéciale, il faut renvoyer l’horloge
node nat() returns (o : int)
let
o = 0 fby (o + 1);
tel
node h() returns (p: bool; o: int)
var x : int;
let
x = nat();
p = true fby not p;
o = x when p;
tel
when false(p)
= when not p