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 !

Référence

[1] : http://www.ada-auth.org/standards/ada22.html/

Commentaires