Comment voir la matrice ?
L’objet de cet article est de montrer au travers d’un exemple complet quelques évolutions apportées par le langage Ada₂₀₂₂. Nous allons faire ici un bref focus sur :
- La généralisation de l’attribut
'Image, - Les nouveaux types d’agrégats pour les tableaux,
- Une utilisation des « Bigs Numbers ».
Attribut 'Image et agrégats [...]
¶
Pour les deux premiers points, nous allons nous pencher sur l’exemple suivant :
with Ada.Numerics.Real_Arrays,
Ada.Text_IO;
use Ada;
procedure Neo is
type Matrix_T is new Numerics.Real_Arrays.Real_Matrix (1 .. 3, 1 .. 3);
Matrix : constant Matrix_T := [for I in Matrix_T'Range (1) =>
[for J in Matrix_T'Range (2) =>
(if I = J then 0.0 else 1.0)]]; --> Ada2022
begin
Text_IO.Put_Line (Matrix'Image); --> Ada2022
Text_IO.Put_Line (Inverse (Matrix)'Image); --> Ada2022
end Neo;
Ici, on peut noter une nouvelle forme d’agrégats pour les tableaux
utilisant les crochets et permettant de faire une initialisation
itérative des éléments de la matrice Matrix. D’autre part, on constate
que l’attribut 'Image est disponible pour le type Matrix_T. En
effet, en Ada₂₀₂₂ l’attribut 'Image est disponible pour n’importe
quel type.
Ce programme produira le résultat suivant sous GNAT :
[
[ 0.00000E+00, 1.00000E+00, 1.00000E+00],
[ 1.00000E+00, 0.00000E+00, 1.00000E+00],
[ 1.00000E+00, 1.00000E+00, 0.00000E+00]]
[
[-5.00000E-01, 5.00000E-01, 5.00000E-01],
[ 5.00000E-01, -5.00000E-01, 5.00000E-01],
[ 5.00000E-01, 5.00000E-01, -5.00000E-01]]
La chaîne de caractères résultant d’une expression utilisant l’attribut
'Image est dépendante de l’implémentation et elle ne correspond pas
obligatoirement à nos attentes. Heureusement, par le biais du nouvel aspect Put_Image, il est possible de redéfinir l’attribut 'Image
d’un type.
L’aspect Put_Image
¶
Le code suivant présente la mise en œuvre de l’aspect Put_Image. Ce
dernier désigne la procédure venant redéfinir le 'Image de notre type
Matrix_T.
with Ada.Strings.Text_Buffers,
Ada.Numerics.Real_Arrays,
Ada.Text_IO;
use Ada,
Ada.Numerics.Real_Arrays;
procedure Neo is
type Matrix_T is new Real_Matrix (1 .. 3, 1 .. 3)
with Put_Image => Matrix_Image; --> Ada2022
procedure Matrix_Image (Output : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class;
Value : in Matrix_T);
procedure Matrix_Image (Output : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class;
Value : in Matrix_T) is separate;
Matrix : constant Matrix_T := [for I in Matrix_T'Range (1) =>
[for J in Matrix_T'Range (2) =>
(if I = J then 0.0 else 1.0)]]; --> Ada2022
begin
Text_IO.Put_Line (Matrix'Image); --> Ada2022
Text_IO.Put_Line (Inverse (Matrix)'Image); --> Ada2022
end Neo;
L’implémentation choisie étant :
with Ada.Strings.Text_Buffers,
Ada.Numerics.Real_Arrays,
Ada.Text_IO;
use Ada,
Ada.Numerics.Real_Arrays;
procedure Neo is
type Matrix_T is new Real_Matrix (1 .. 3, 1 .. 3)
with Put_Image => Matrix_Image; --> Ada2022
procedure Matrix_Image (Output : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class;
Value : in Matrix_T);
procedure Matrix_Image (Output : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class;
Value : in Matrix_T) is separate;
Matrix : constant Matrix_T := [for I in Matrix_T'Range (1) =>
[for J in Matrix_T'Range (2) =>
(if I = J then 0.0 else 1.0)]]; --> Ada2022
begin
Text_IO.Put_Line (Matrix'Image); --> Ada2022
Text_IO.Put_Line (Inverse (Matrix)'Image); --> Ada2022
end Neo;
Indépendamment du compilateur, ce programme produira le résultat suivant :
[[ 0.00000E+00, 1.00000E+00, 1.00000E+00]
[ 1.00000E+00, 0.00000E+00, 1.00000E+00]
[ 1.00000E+00, 1.00000E+00, 0.00000E+00]]
[[-5.00000E-01, 5.00000E-01, 5.00000E-01]
[ 5.00000E-01,-5.00000E-01, 5.00000E-01]
[ 5.00000E-01, 5.00000E-01,-5.00000E-01]]
C’est mieux, mais cela n’est pas encore ça ! En effet, nous aurions aimé avoir :
[[ 0/ 1, 1/ 1, 1/ 1]
[ 1/ 1, 0/ 1, 1/ 1]
[ 1/ 1, 1/ 1, 0/ 1]]
[[-1/ 2, 1/ 2, 1/ 2]
[ 1/ 2,-1/ 2, 1/ 2]
[ 1/ 2, 1/ 2,-1/ 2]]
Qu’à cela ne tienne ! Nous allons utiliser la hiérarchie
Ada.Numerics.Big_Numbers.
La hiérarchie Ada.Numerics.Big_Numbers
¶
Cette hiérarchie a été introduite en Ada₂₀₂₂ afin de permettre de
faire de l’arithmétique entière non contrainte par l’architecture de la
machine cible au travers du type
Ada.Numerics.Big_Numbers.Big_Integers.Big_Integer. Cette hiérarchie
permet aussi de faire de l’arithmétique sur des nombres réels avec une
précision arbitraire par le biais du paquetage
Ada.Numerics.Big_Numbers.Big_Reals.Big_Real.
C’est ce dernier type qui va nous intéresser ici.
Pour satisfaire cette capacité à avoir une précision arbitraire, le type
Big_Real va être représenté en interne sous une forme fractionnaire.
Ainsi donc, 0,33333333… serait représenté par 1/3.
Le paquetage concerné est proche de :
package Ada.Numerics.Big_Numbers.Big_Reals is
type Big_Real is private;
function Is_Valid (Arg : Big_Real) return Boolean;
subtype Valid_Big_Real is Big_Real
with Dynamic_Predicate => Is_Valid (Valid_Big_Real),
Predicate_Failure => raise Program_Error;
function Numerator (Arg : Valid_Big_Real) return Big_Integers.Valid_Big_Integer;
function Denominator (Arg : Valid_Big_Real) return Big_Integers.Big_Positive;
function "+" (L, R : Valid_Big_Real) return Valid_Big_Real;
function "-" (L, R : Valid_Big_Real) return Valid_Big_Real;
function "*" (L, R : Valid_Big_Real) return Valid_Big_Real;
function "/" (L, R : Valid_Big_Real) return Valid_Big_Real;
function "**" (L : Valid_Big_Real; R : Integer) return Valid_Big_Real;
generic
type Num is digits <>;
package Float_Conversions is
function To_Big_Real (Arg : Num) return Valid_Big_Real;
end Float_Conversions;
private
end Ada.Numerics.Big_Numbers.Big_Reals;
Nous allons donc modifier notre implémentation pour afficher les éléments de notre matrice sous forme fractionnaire. Nous en profiterons pour mettre aussi en œuvre le symbole @ agissant comme l’équivalent de la partie gauche d’une affectation :
with Ada.Strings.Unbounded,
Ada.Numerics.Big_Numbers.Big_Reals, --> Ada2022
Ada.Numerics.Big_Numbers.Big_Integers, --> Ada2022
Ada.Characters.Latin_1;
use Ada.Strings.Unbounded,
Ada.Numerics.Big_Numbers.Big_Reals,
Ada.Numerics.Big_Numbers.Big_Integers;
separate (Neo)
procedure Matrix_Image (Output : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class;
Value : in Matrix_T) is
package To_BR is new Float_Conversions (Num => Float);
use To_BR;
Result : Unbounded_String := To_Unbounded_String ("[");
LF renames Ada.Characters.Latin_1.LF; --> Ada2022
begin
for I in Matrix_T'Range (1) loop
Result := @ & (if I = Matrix_T'First (1) then "" else " ") & "[";
for J in Matrix_T'Range (2) loop
declare
Tmp : constant Valid_Big_Real := To_Big_Real (Value (I, J));
N : constant Valid_Big_Integer := Numerator (Tmp);
D : constant Big_Positive := Denominator (Tmp);
Str : constant String := N'Image & "/" & D'Image;
begin
Result := @ & Str & (if J = Matrix_T'Last (2) then "" else ",");
end;
end loop;
Result := @ & "]" & (if I = Matrix_T'Last (1) then "" else [LF]);
end loop;
Output.Put (To_String (Result & "]"));
end Matrix_Image;
Et le résultat sera bien conforme à l’attendu :
[[ 0/ 1, 1/ 1, 1/ 1]
[ 1/ 1, 0/ 1, 1/ 1]
[ 1/ 1, 1/ 1, 0/ 1]]
[[-1/ 2, 1/ 2, 1/ 2]
[ 1/ 2,-1/ 2, 1/ 2]
[ 1/ 2, 1/ 2,-1/ 2]]
Conclusion¶
Sans être une profonde évolution du langage Ada comme l’a été Ada₂₀₁₂,
Ada₂₀₂₂ apporte beaucoup de « petites » améliorations qui facilitent
la vie au quotidien. Par exemple, la généralisation de l’attribut
'Image, va permettre de mettre simplement en place des
traces permettant de voir la Matrice !
