ADA 95教程 记录
我们第一次看记录
Example program ------> e_c12_p1.ada
-- Chapter 12 - Program 1 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Record1 is type DATE is record Month : INTEGER range 1..12; Day : INTEGER range 1..31; Year : INTEGER range 1776..2010; end record; Independence_Day : DATE; Birth_Day : DATE; Today,Pay_Day : DATE := (5, 25, 1982); begin Independence_Day.Month := 7; Independence_Day.Day := 4; Independence_Day.Year := 1776; Birth_Day := Independence_Day; Pay_Day.Day := 30; Put("Independence day was on "); Put(Independence_Day.Month, 2); Put("/"); Put(Independence_Day.Day, 2); Put("/"); Put(Independence_Day.Year, 4); New_Line; Birth_Day := (Day => 19, Month => 2, Year => 1937); Today := (7, 14, 1952); Pay_Day := (7, Year => 1954, Day => 17); end Record1; -- Result of execution -- Independence day was on 7/ 4/1776
Ada提供了两种复合类型,array(我们之前研究过)和record(这是本章的主题)。检查名为e_c12_p1.ada的程序,以获取我们的第一个记录示例。
第7行到第12行声明了一个Ada记录,它实际上只声明了一个类型。通常使用类型的语法,但是当我们谈到类型定义本身时,我们从保留字record开始。然后插入记录的组件,记录类型定义由保留字end record终止。
记录可以包含任何所需的组件,唯一的要求是必须在此定义之前声明组件类型,并且记录类型不能作为其自身的组件包含。关于记录要记住的关键点是,数组是由一些相似的元素组成的,而记录是由一些不同类型的组件组成的。
记录里有什么?
不可能像在Pascal中那样声明匿名record类型。在变量声明中使用之前,记录必须是命名类型。
在这个记录中,我们有一个名为Month的变量,它允许存储从1到12的任何值,显然代表一年中的月份。还有Day and Year变量,每个变量与Month 不同,因为对每个变量施加了不同的约束。在声明了记录类型之后,我们仍然没有实际的变量,只有一个类型,但是在第14到16行中,我们声明了四个DATE类型的变量。因为变量的类型是DATE,所以每个变量都有三个组成部分,即Month, Day, and Year.。注意,其中两个变量按照变量定义的顺序初始化为括号中给出的值。因此,对于两个初始化变量 Today and Pay_Day.,Month设置为5,Day设置为25,Year设置为1982。在我们讨论程序本身之后,初始化将非常清楚,因此稍后我们将回到它。
我们如何使用这些记录?
由于Independence_Day实际上是一个由三个不同的变量组成的变量,我们需要一种方法来告诉计算机我们感兴趣的子领域。我们通过将主变量名和子字段与第19行到第21行所示的点相结合来实现这一点。这在Ada中称为选定组件表示法。你应该清楚 Independence_Day. Month 实际上是一个能够存储整数类型的变量,只要它在1到12之间。记录的三个元素是三个简单变量,可以在程序中使用,只要可以使用任何其他整数类型的变量。为了方便我们,这三个节日被分在一起,因为它们定义了一个日期,我们称之为Independence_Day。数据可以存储在三个简单的变量中,我们可以用通常处理数据的方式跟踪它们,但是记录允许更方便的分组和一些附加操作。
记录分配
使用记录有一个很大的优点,如第23行所示,与变量Independence_Day相关的所有三个值都被分配给变量 Birth_Day的三个相应组成部分。如果它们是独立的变量,则必须一次复制一个。Pay_Day 的Day字段在第25行中被分配了一个新值,Independence_Day中包含的日期显示在监视器上,以便于说明。
命名聚合和位置聚合
第35行有一个使用命名聚合的赋值示例,其中三个字段用各自的名称和指针运算符定义。它可以理解为,“名为Day的变量的值为19,Month的值为2,依此类推”。由于它们是命名的,因此不要求它们与记录定义中的顺序相同,但可以是任意顺序。使用命名聚合表示法的真正优点是,所有元素都已命名,而且很清楚每个变量字段的赋值是什么。应该指出,聚合是一组数据,这些数据可能属于或不属于同一类型。
第36行通过简单地给出三个值来定义记录的三个值,但是在这种情况下,这三个元素的顺序必须正确,这样编译器就能够将它们分配到正确的子字段。这称为位置聚合。这是用于初始化第16行中日期的聚合类型。
混合聚合
第37行说明了混合聚合的用法,其中一些由其位置定义,其余由其名称定义。位置定义必须放在第一位,在给定一个命名变量后,其余变量也必须命名。必须记住的一点是,必须提及所有值,即使其中一些值不会更改。这似乎是一个挑剔的麻烦,但它大大简化了编译器编写人员的工作。
编译并运行这个程序,你的显示器上就会显示Independence_Day的日期。一定要理解这个程序,因为理解下一个程序需要你彻底理解这个程序。应该清楚的是,无论您使用命名、位置或混合表示法,都需要为每个参数使用正确的类型。
包含记录的记录
Example program ------> e_c12_p2.ada
-- Chapter 12 - Program 2 with Ada.Text_IO; use Ada.Text_IO; procedure Record2 is type DATE is record Month : INTEGER range 1..12; Day : INTEGER range 1..31; Year : INTEGER range 1776..2010; end record; type PERSON is record Name : STRING(1..15); Birth_Day : DATE; Age : INTEGER; Sex : CHARACTER; end record; Self, Mother, Father : PERSON; My_Birth_Year : INTEGER renames Self.Birth_Day.Year; begin Self.Name := "John Q. Doe "; Self.Age := 21; Self.Sex := 'M'; Self.Birth_Day.Month := 10; Self.Birth_Day.Day := 18; Self.Birth_Day.Year := 1938; My_Birth_Year := 1938; -- Identical to previous statement Mother := Self; Father.Birth_Day := Mother.Birth_Day; Father.Birth_Day.Month := Self.Birth_Day.Month - 4; Mother.Sex := 'F'; if Mother /= Self then Put_Line("Mother is not equal to Self."); end if; end Record2; -- Result of execution -- Mother is not equal to Self.
检查名为e_c12_p2.ada的文件,以获取包含另一条记录的记录声明示例。我们以与上一个程序完全相同的方式声明记录类型DATE,但是我们继续声明另一个名为PERSON的记录类型。您将注意到,新记录由四个变量组成,其中一个变量的类型是DATE,而DATE本身包含三个变量。因此,我们声明了一个包含三个简单变量的记录和一个包含三个以上变量的变量记录,从而在这个记录类型中总共有六个单独的变量。在第22行中,我们声明了三个变量,每个变量由六个简单变量组成,因此在我们的示例程序中有18个声明的变量要处理。
如何使用复合记录
第28行到第30行应该不会给您带来真正的问题,因为我们使用的是在上一个示例程序中获得的知识,但是要指定日期需要对Ada知识库进行另一个扩展。请注意,除了主变量Self的名称之外,我们还必须提到Birth_Day变量,它是其中的一部分,最后是Birth_Day变量的子字段Month,在第31行中。因此,变量名由三个名称组成,“点”在一起,形成一个唯一的简单变量名。同样,这被称为选定组件表示法。第32行到第34行为变量Self的其余三个字段分配了一些有意义的数据。第36行在一个简单语句中将变量Self的所有六个元素赋给变量Mother。第37行仅用于将母亲出生日的三个分量的值指定给父亲出生日的三个相应分量。
由于每个子字段实际上都是一个简单的变量,因此每个子字段都可以用于计算,如第38行所示,在该行中,母亲的出生日月份被指定为比自己的出生日月份小4的值。这样做只是为了说明,只要遵循简单类型的规则,简单变量就可以按您所希望的任何方式使用。
重命名记录组件
第24行说明了如何重命名记录的组件,以减轻每次使用字段时输入虚线表示法的问题。在本例中,简单名称My_Birth_Year是所有三个组件所需扩展名称的同义词。必须再次指出,这只会影响编译速度,因为它只是一个附加名称,可以用来引用变量。还必须重申的是,除非在少数情况下,该设施确实增加了程序的清晰度,否则不应使用该设施。新名称的正确用法如第34行所示。
记录分配和比较
如第36、37和41行所示,可以将整个记录分配给相同类型的其他记录,并且可以比较相同类型的整个记录是否相等或不相等。只有当一条记录的每个子字段与另一条记录的相应子字段相等时,记录才相等。其他比较运算符在Ada中不可用于记录。
编译并运行这个程序,即使你不会得到任何输出。自己添加一些输出语句,看看是否可以将一些数据输出到监视器。
记录中的数组
Example program ------> e_c12_p3.ada
-- Chapter 12 - Program 3 with Ada.Text_IO; use Ada.Text_IO; procedure Record3 is type MONTH_NAME is (JAN,FEB,MAR,APR,MAY,JUN,JUL, AUG,SEP,OCT,NOV,DEC); type DATE is record Month : MONTH_NAME; Day : INTEGER range 1..31; Year : INTEGER range 1776..2010; end record; type GRADE_ARRAY is array(1..4) of POSITIVE; type PERSON is record Name : STRING(1..15); Birth_Day : DATE; Graduation_Day : DATE := (MAY,27,1987); Age : INTEGER := 21; Sex : CHARACTER := 'F'; Grades : GRADE_ARRAY; end record; Self, Mother, Father : PERSON; begin Self.Name := "John Q. Doe "; Self.Sex := 'M'; Self.Birth_Day.Month := OCT; Self.Birth_Day.Day := 18; Self.Birth_Day.Year := 1938; Self.Grades(1) := 85; Self.Grades(2) := 90; Self.Grades(3) := 75; Self.Grades(4) := 92; Mother := Self; Father.Birth_Day := Mother.Birth_Day; Father.Birth_Day.Day := Self.Birth_Day.Day - 4; Mother.Sex := 'F'; end Record3; -- Result of execution -- (No output from this program.)
检查名为e_c12_p3.ada的文件,您将发现在第17行中声明的数组类型,然后在记录类型PERSON中使用。添加允许PERSON类型的变量存储四个等级,与上一个程序相比,这给了我们一点额外的灵活性。将数据分配给新字段的方法如第38行到第41行所示,不需要额外的注释,因为您已经精通如何使用数组。这里必须提到一条规则,不允许在记录中声明匿名类型的数组,它必须命名。一定要编译并运行这个程序。
您将注意到,第23行到第25行为记录的某些元素指定了默认值。每次声明此类型的记录时,都会为这些元素指定默认值。当然,程序员可以通过分配任何他想要的值来立即覆盖默认值,但是默认值将用于初始化那些特定的成员。
一组记录
-- Chapter 12 - Program 4 with Ada.Text_IO; use Ada.Text_IO; procedure Record4 is type MONTH_NAME is (JAN,FEB,MAR,APR,MAY,JUN,JUL, AUG,SEP,OCT,NOV,DEC); type DATE is record Month : MONTH_NAME; Day : INTEGER range 1..31; Year : INTEGER range 1776..2010; end record; type PERSON is record Name : STRING(1..15); Birth_Day : DATE; Age : INTEGER := 15; Sex : CHARACTER := 'M'; end record; Teacher : PERSON; Class_Member : array(1..35) of PERSON; Standard : constant PERSON := ("John Q. Doe ", (MAR, 27, 1955), 33, 'M'); type EMPTY_RECORD is record null; end record; type ANOTHER_EMPTY_RECORD is null record; begin Teacher.Name := "John Q. Doe "; Teacher.Age := 21; Teacher.Sex := 'M'; Teacher.Birth_Day.Month := OCT; Teacher.Birth_Day.Day := 18; Teacher.Birth_Day.Year := 1938; for Index in Class_Member'RANGE loop Class_Member(Index).Name := "Suzie Lou Q "; Class_Member(Index).Birth_Day.Month := MAY; Class_Member(Index).Birth_Day.Day := 23; Class_Member(Index).Birth_Day.Year := 1956; Class_Member(Index).Sex := 'F'; end loop; Class_Member(4).Name := "Little Johhny "; Class_Member(4).Sex := 'M'; Class_Member(4).Birth_Day.Day := 17; Class_Member(7).Age := 14; Class_Member(2) := Standard; Class_Member(3) := Standard; end Record4; -- Result of execution -- (No output from this program.)
检查名为e_c12_p4.ada的文件以获取记录数组的示例。类型DATE和PERSON的声明方式类似于它们在e_c12_p2.ada中的声明,但是在第26行中,我们声明了一个由35个变量组成的数组,每个变量都是PERSON类型,因此每个变量都由6个变量字段组成。在第46行到第52行中,我们使用循环将一些无意义数据分配给35个变量的每个字段。最后,我们将无意义的数据分配给一些变量,以说明如何在第54行到第59行中完成。你理解这个程序应该没有问题。
请注意,我们可以将数据分配给其中一个记录,例如第一个记录,然后在循环中使用它,通过使用诸如“Class_Member(Index):=Class_Member(1);”之类的记录分配,并从2循环到35,将值分配给所有其他记录。在一个有用的程序中,要分配的数据将来自某个文件,因为我们可能正在填充数据库。事实上,这是一个非常粗糙的数据库的开始。
第27行和第28行演示了另一个新构造,其中我们将名为Standard的变量初始化为给定的聚合。请注意,与unnested记录要求一样,嵌套记录必须使用包含所有值的聚合进行初始化。第30行到第33行演示了一种定义null记录的方法,在本教程后面研究继承时,这将非常有用。编译并运行这个程序,以确保它确实会编译并运行。
在这个时候,您最好检查一下ARM的第3.8节,以熟悉它的语言和这里使用的定义方法。
变体记录
ADA95提供了变体记录,第20章将详细介绍它,但它在很大程度上被ADA95提供的更强大的继承和类型扩展技术所取代。
编程练习
1.重写e_c12_p4.ada并将Standard的初始化聚合从位置聚合更改为命名聚合。(Solution)
2.重写e_c12_p1.ada以使用e\\u c12\\u p3.ada中使用的月份字段的枚举类型。请注意,您需要实例化名为Enumerated\u IO的包的副本,以显示月份名称(Solution)
---------------------------------------------------------------------------------------------------------------------------
原英文版出处:https://perso.telecom-paristech.fr/pautet/Ada95/a95list.htm
翻译(百度):博客园 一个默默的 *** 的人