ADA 95教程 高级特性 通用包

我们的第一个通用包

在上一章中,我们研究了泛型子程序的使用。本章将专门研究泛型包,我们将详细讨论这个主题,因为只有过程、函数和包可以作为泛型单元使用。

Example program ------> e_c31_p1.ada

                                    -- Chapter 31 - Program 1
generic
   type MY_INT_TYPE is (<>);
   type MY_REAL_TYPE is digits <>;
package EasyPkg is
   procedure Trade_Values (X, Y : in out MY_INT_TYPE);
   function Average_Values (X, Y : MY_REAL_TYPE)
                                       return MY_REAL_TYPE;
end EasyPkg;


package body EasyPkg is
   procedure Trade_Values (X, Y : in out MY_INT_TYPE) is
   Temp : MY_INT_TYPE;
   begin
      Temp := X;
      X := Y;
      Y := Temp;
   end Trade_Values;

   function Average_Values (X, Y : MY_REAL_TYPE)
                                       return MY_REAL_TYPE is
   begin
      return (X + Y) / 2.0;
   end Average_Values;

end EasyPkg;



with Ada.Text_IO, EasyPkg;
use Ada.Text_IO;

procedure GenPkg is

type MY_NEW_TYPE is new INTEGER range -12..123;
type MY_NEW_FLOAT is new FLOAT digits 6;

package Funny_Stuff is new EasyPkg(MY_NEW_TYPE, MY_NEW_FLOAT);
use Funny_Stuff;
package Usual_Stuff is new EasyPkg(INTEGER, FLOAT);
use Usual_Stuff;

Int1 : INTEGER := 12;
Int2 : INTEGER := 35;
My_Int1 : MY_NEW_TYPE := 1;
My_Int2 : MY_NEW_TYPE := 14;

Real1 : FLOAT;
My_Real1 : MY_NEW_FLOAT;

begin
   Trade_Values(Int1, Int2);       -- Uses Usual_Stuff.Trade_Values
   Trade_Values(My_Int1, My_Int2); -- Uses Funny_Stuff.Trade_Values
   Usual_Stuff.Trade_Values(Int1, Int2);
   Funny_Stuff.Trade_Values(My_Int1, My_Int2);
-- Usual_Stuff.Trade_Values(My_Int1, My_Int2);   -- Illegal
-- Trade_Values(My_Int1, Int2);                  -- Illegal

   Real1 := Average_Values(2.71828, 3.141592);
   Real1 := Average_Values(Real1, 2.0 * 3.141592);
   My_Real1 := Average_Values(12.3, 27.345);
   My_Real1 := Average_Values(My_Real1, 2.0 * 3.141592);
   My_Real1 := Funny_Stuff.Average_Values(12.3, 27.345);
end GenPkg;




-- Result of execution

-- (There is no output from this program)

 

检查名为e_c31_p1的文件。Ada为我们的第一个泛型包示例。这个包被命名为EasyPkg,因为它很容易理解,并且由一个过程和一个函数组成。泛型包以保留字generic开头,后面是两个泛型形式参数,以及Ada包规范的标准格式(第5行到第9行)。第一个泛型形式参数是离散类型,第二个是浮点类型,我们在上一章讨论过。

主程序的第39行和第41行给出了实例化语句,除了第一个单词,即保留单词package外,它们与上一章中使用的语句没有什么不同,因为在本例中我们声明的是一个包的实例。如果我们用每个实例化语句中声明的类型替换正式参数类型,就会得到一个正常的包,就像我们之前研究的那样。在包的情况下,我们可以像第40行和第42行那样添加use子句,从而消除了对扩展命名符号(通常称为点符号)的需要。在声明了几个要使用的变量之后,我们就可以执行刚才声明的两个过程和函数了。您应该清楚,我们在名为Funny_Stuff的包中有一个过程和一个函数,在名为Usual_Stuff的包中有另一个过程和函数。

在通用包中使用use子句时,必须注意一点。在通用包本身中使用use子句是不合法的。每个实例化都必须显式地给出泛型包的扩展名。当然,这只发生在嵌套泛型包中,这是下一个示例程序的主题。然而,use子句是合法的,可用于通用包的任何和每个实例化副本。实例化的包的新名称不能重载,因为它不允许重载任何包的名称。

 

使用实例化的包

在第53行中,我们将过程命名为Trade_Values,但是由于有两个过程具有这个名称,Ada将通过比较类型来选择正确的过程。这是我们的老朋友,又叫重载。在第54行中,由于使用的类型不同,将使用注释中指出的其他过程。在第55和56行中,我们显式地告诉系统要使用哪个重载,但是它仍然会检查类型以确定它们是否兼容。在第57行,我们告诉系统使用错误的类型,这会导致类型不匹配和编译错误,在第58行,我们使用两种不同的类型作为参数,这是另一种类型不匹配。(第57行和第58行被注释掉了,因此我们实际上不会得到错误,但是您应该删除注释,以查看编译器是否报告了错误。)

第60到64行给出了正确使用上面实例化的两个函数的例子,并说明了universal_real类型的字面量与函数的两个副本是兼容的,正如您所期望的那样。编译器使用赋值语句左边的赋值变量的类型来决定每个语句使用哪个重载。

在您花足够的时间来理解这个程序之后,编译并执行它,即使它没有输出。

 

嵌套通用包

Example program ------> e_c31_p2.ada

                                    -- Chapter 31 - Program 2
package EasyPkg is
   procedure Trade_Values (X, Y : in out INTEGER);

   generic
      type MY_REAL_TYPE is digits <>;
   package Nested_Generic is
      function Average_Values (X, Y : MY_REAL_TYPE)
                                          return MY_REAL_TYPE;
   end Nested_Generic;
end EasyPkg;


package body EasyPkg is
   procedure Trade_Values (X, Y : in out INTEGER) is
   Temp : INTEGER;
   begin
      Temp := X;
      X := Y;
      Y := Temp;
   end Trade_Values;

   package body Nested_Generic is
      function Average_Values (X, Y : MY_REAL_TYPE)
                                          return MY_REAL_TYPE is
      begin
         return (X + Y) / 2.0;
      end Average_Values;
   end Nested_Generic;

end EasyPkg;



with Ada.Text_IO, EasyPkg;
use Ada.Text_IO, EasyPkg;

procedure NestPkg is

type MY_NEW_FLOAT is new FLOAT digits 6;

package Funny_Stuff is new EasyPkg.Nested_Generic(MY_NEW_FLOAT);
use Funny_Stuff;
package Usual_Stuff is new Nested_Generic(FLOAT);
use Usual_Stuff;

Int1 : INTEGER := 12;
Int2 : INTEGER := 35;

Real1 : FLOAT;
My_Real1 : MY_NEW_FLOAT;

begin
   Trade_Values(Int1, Int2);         -- Uses Trade_Values directly
   EasyPkg.Trade_Values(Int1, Int2); -- Uses Trade_Values directly
-- Usual_Stuff.Trade_Values(Int1, Int2);    -- Illegal
-- Funny_Stuff.Trade_Values(Int1, Int2);    -- Illegal

   Real1 := Average_Values(2.71828, 3.141592);
   Real1 := Average_Values(Real1, 2.0 * 3.141592);
   My_Real1 := Average_Values(12.3, 27.345);
   My_Real1 := Average_Values(My_Real1, 2.0 * 3.141592);
   My_Real1 := Funny_Stuff.Average_Values(12.3, 27.345);
end NestPkg;




-- Result of execution

-- (There is no output from this program)

 

示例程序名为e_c31_p2。Ada包含一个嵌套在另一个非泛型包中的泛型包。外部包包含单个过程,嵌套泛型包包含单个浮点类型的正式泛型参数和单个函数。程序和功能与上一个程序相同,但为了便于说明,这里的组织方式不同。

请注意,由于过程不在外部包的通用部分中,所以它可以像在没有通用部分的包中一样直接可用。包名可以在use子句中声明,这将使包的非泛型部分可用。

程序的可执行部分非常简单,与上一个示例程序非常相似,因此它将留给学生来学习、编译和执行这个程序。

 

对象作为通用形式参数

Example program ------> e_c31_p3.ada

                                   -- Chapter 31 - Program 3
generic
   type ITEM is range <>;
   ROWS    : in POSITIVE := 8;
   COLUMNS : in POSITIVE := 12;
package Matrix_Operations is

   type LOCAL_MATRIX is private;

   procedure Clear_Matrix(Matrix_To_Clear : in out LOCAL_MATRIX);

   function Add_Matrices(M1, M2 : LOCAL_MATRIX) return LOCAL_MATRIX;

private
   type LOCAL_MATRIX is array(POSITIVE range 1..ROWS,
                              POSITIVE range 1..COLUMNS) of ITEM;
end Matrix_Operations;




with Ada.Text_IO;
use Ada.Text_IO;
package body Matrix_Operations is

   procedure Clear_Matrix(Matrix_To_Clear : in out LOCAL_MATRIX) is
   begin
      for Row in 1..Matrix_To_Clear'LAST(1) loop
         for column in 1.. Matrix_To_Clear'LAST(2) loop
            Matrix_To_Clear(Row,Column) := 0;
         end loop;
      end loop;
      Put_Line("  A matrix has been cleared");
   end Clear_Matrix;

   function Add_Matrices(M1, M2 : LOCAL_MATRIX) return LOCAL_MATRIX is
   Temp : LOCAL_MATRIX;
   begin
      for Row in 1..M1'LAST(1) loop
         for column in 1.. M1'LAST(2) loop
            Temp(Row, Column) := M1(Row, Column) + M2(Row, Column);
         end loop;
      end loop;
      Put_Line("  Two matrices have been summed");
      return Temp;
   end Add_Matrices;

end Matrix_Operations;




with Ada.Text_IO, Matrix_Operations;
use Ada.Text_IO;

procedure ObjGen is

package Cookie_Jar is new Matrix_Operations(NATURAL, 3, 5);
use Cookie_Jar;
package Puppies is new Matrix_Operations(INTEGER);
package Animals is new Matrix_Operations(COLUMNS => 7,
                                         ROWS => 11,
                                         ITEM => INTEGER);
use Animals;

Cookie_Matrix                : Cookie_Jar.LOCAL_MATRIX;
Dog_Matrix                   : Puppies.LOCAL_MATRIX;
Cattle, Jerseys, Black_Angus : Animals.LOCAL_MATRIX;

begin
   Put_Line("Begin the matrix operations.");
   Clear_Matrix(Cookie_Matrix);
   Puppies.Clear_Matrix(Dog_Matrix);
   Clear_Matrix(Jerseys);
   Clear_Matrix(Black_Angus);
   Cattle := Add_Matrices(Jerseys, Black_Angus);

end ObjGen;




-- Result of execution

-- Begin the matrix operations
--   A matrix has been cleared
--   A matrix has been cleared
--   A matrix has been cleared
--   A matrix has been cleared
--   Two matrices have been summed

 

检查名为e_c31_p3的程序。使用对象作为形式泛型参数而不仅仅是类型的例子。在这种情况下,ROWS的数量和column的数量将成为实例化的一部分,结果过程和函数将可以使用所需的大小矩阵。此外,名为ROWS的常量和名为COLUMNS的常量被初始化为第4行和第5行中给出的值。如果在实例化时没有给出一个值,那么这些值的使用方式就像在过程或函数中使用默认值一样。

在本例中,包主体使用标准Ada.Text_IO包,并在每次调用某个子程序时向监视器输出一个字符串。这样做只是为了向您说明包Ada.Text_IO没有什么神奇之处,并且可以在另一个包中使用。

 

使用的对象

在第58行中,使用位置聚合表示法实例化了包,矩阵大小的值分别为3和5。第60行说明了上面通用部分中声明的默认值的使用,第61行说明了包实例化过程中使用的命名聚合表示法。

 

可以使用导出的类型

回到第8行,我们在包规范中声明了名为LOCAL_MATRIX的类型,因此任何调用程序都可以使用它。如果您引用包规范的私有部分,您将看到类型LOCAL_MATRIX被声明为正式泛型对象的函数。在实例化包的一个副本之后,就可以在调用程序中使用导出的类型。我们使用第66行到第68行导出的类型来声明一些变量,但是即使有两个实例使用了use子句,我们也必须通过使用扩展命名表示法显式声明所需的类型。这是因为编译器无法知道我们想为每个变量使用哪个导出类型。

通过上面对这个程序中新概念的描述,您应该能够理解它的细节。请确保编译并执行此程序。

 

作为泛型参数的过程

Example program ------> e_c31_p4.ada

                                     -- Chapter 31 - Program 4
generic
   type ITEM is range <>;
   with procedure Funny_Average(A, B, C : ITEM;
                                Result  : in out ITEM);
package Dis_Avg is
   procedure Display_Funny_Average(A, B, C : ITEM);
end Dis_Avg;




with Ada.Text_IO;
use Ada.Text_IO;

package body Dis_Avg is

   package Int_IO is new Ada.Text_IO.Integer_IO(ITEM);
   use Int_IO;

   procedure Display_Funny_Average(A, B, C : ITEM) is
   Mean : ITEM;
   begin
      Funny_Average(A, B, C, Mean);
      Put("The funny average is ");
      Put(Mean, 5);
      New_Line;
   end Display_Funny_Average;
end Dis_Avg;



with Dis_Avg;

procedure ProcPkg is

   procedure R_Average(First, Second, Third : INTEGER;
                       Weighted_Average : in out INTEGER) is
   begin
      Weighted_Average := (First + 2 * Second + Third) / 4;
   end R_Average;

   package Averages is new Dis_Avg(INTEGER, R_Average);

begin
   Averages.Display_Funny_Average(1, 3, 5);
   Averages.Display_Funny_Average(1, 3, 17);
   Averages.Display_Funny_Average(3, 17, 5);
end ProcPkg;




-- Result of execution

-- The funny average is    3
-- The funny average is    6
-- The funny average is   10

检查名为e_c31_p4的程序。Ada作为一个过程作为通用形式参数的例子。语法在第4行中使用保留字with作为形式形参的开头,该过程的完整头文件包含形式形参及其类型的列表。然后可以从包体内部调用此过程,并将引用实际位于通用包外部的过程。现在我们必须定义将被调用的过程,以满足对这个形式参数的调用。稍后我们将看到在哪里定义这个过程。

请参阅主程序,其中声明了名为R_Average的过程。它具有与泛型形参中声明的完全相同的形参结构,因此可以在第43行中的实例化调用中使用它。在通用实例化中对过程的调用实际上会导致对调用程序中的过程的回调,以执行一些计算。

 

这可以用来做什么?

这个功能使您能够编写一个泛型包,该包可以在几个地方使用,但每个地方略有不同,因为每个实例化都可以使用一个不同的过程来引用回调用程序。这只是增加Ada灵活性的另一个可用实体。理解了这里的逻辑之后,应该编译并执行这个程序。

 

作为泛型参数的函数

Example program ------> e_c31_p5.ada

                                     -- Chapter 31 - Program 5
generic
   type ITEM is range <>;
   with function Funny_Average(A, B, C : ITEM) return ITEM;
package Dis_Avg is
   procedure Display_Funny_Average(A, B, C : ITEM);
end Dis_Avg;



with Ada.Text_IO;
use Ada.Text_IO;

package body Dis_Avg is

   package Int_IO is new Ada.Text_IO.Integer_IO(ITEM);
   use Int_IO;

   procedure Display_Funny_Average(A, B, C : ITEM) is

   Mean : ITEM;

   begin
      Mean := Funny_Average(A, B, C);
      Put("The funny average is ");
      Put(Mean, 5);
      New_Line;
   end Display_Funny_Average;
end Dis_Avg;



with Dis_Avg;

procedure FuncPkg is

   function R_Average(First, Second, Third : INTEGER)
                                             return INTEGER is
   begin
      return (First + 2 * Second + Third) / 4;
   end R_Average;

   package Averages is new Dis_Avg(INTEGER, R_Average);

begin
   Averages.Display_Funny_Average(1, 3, 5);
   Averages.Display_Funny_Average(1, 3, 17);
   Averages.Display_Funny_Average(3, 17, 5);
end FuncPkg;




-- Result of execution

-- The funny average is     3
-- The funny average is     6
-- The funny average is    10

 

检查名为e_c31_p5的程序。Ada作为一个使用函数作为通用形式参数的例子。该逻辑与上一个程序中描述的相似,除了一个函数用于返回调用程序的引用。没有进一步的解释,因为应该不需要。对于几个过程或函数或它们的组合可以用作通用形式参数,您不应该感到太惊讶。可以根据需要组合任何孤立的示例以实现预期的目标。可以根据需要嵌套通用包和普通包。您将决定如何模块化和打包您的程序,以最好地实现所述的目标。艾达给了你很多选择。请确保编译并执行此程序。

 

编程练习

1.向e_c31_p1添加枚举类型。实例化EasyPkg的一个副本,并使用它交换枚举类型的一些值。在实例化浮点类型时,必须使用枚举类型提供浮点类型。(Solution)

2.e_c16_p1进行转换。Ada从本教程第16章变成了一个可以与任何离散类型一起使用的泛型包。修改e_c16_p2。从同一章的ada实例化并使用枚举类型和INTEGER类型。(Solution 1)(Solution 2)

 

---------------------------------------------------------------------------------------------------------------------------

原英文版出处:https://perso.telecom-paristech.fr/pautet/Ada95/a95list.htm

翻译(百度):博客园  一个默默的 *** 的人

posted @ 2021-04-28 07:11  yangjianfeng  阅读(73)  评论(0编辑  收藏  举报