我们来谈谈类型
在本教程中,我们一直在讨论类型,但我们一直在努力解决这些问题,而不是仔细定义Ada中可用的各种类型。本章将专门讨论类型。在我们介绍了一些关于各种标量类型以及如何使用它们的资料之前,我们不可能完整地讨论类型,但是在这些资料之后,我们将全面讨论类型的主题。
声明类型有四种不同的方法。您可能会猜到,我们将在本章中详细讨论每种方法。具体如下:
1.预定义类型。这些是由编译器编写者提供的,如ADA95参考手册(ARM)中所定义。我们已经使用了其中的一些。
2.用户定义的类型。我们已经在一些早期程序中定义了一些新类型。
3.派生类型。它们之所以得名,是因为它们部分是基于先前定义的类型定义的,并从这些类型派生出它们的一些特性。我们还没有遇到派生类型。
4.子类型。它们通常是它们所基于的另一种类型的子集。我们在上一章中遇到了子类型。
类型定义一组值和一组基本操作。一个类型的基本操作是:
-固有的预定义操作,如赋值、加法、减法等。
-对于派生类型,从父级继承的任何操作。我们将在本章研究派生类型。
-具有一个或多个参数的子程序,或在与类型相同的包中声明的类型的结果。
ADA中的预定义类型
ARM要求每个Ada编译器提供几个预定义的类型,包括INTEGER、NATURAL、POSITIVE、FLOAT、CHARACTER和STRING。其他几种类型是可选的,包括LONG_ INTEGER、SHORT_INTEGER、LONG_FLOAT和SHORT_FLOAT。它将留给您研究编译器附带的文档,并查看编译器包中包含哪些可选类型。您可以在附录M中找到这些信息。这些类型的实际定义在名为Standard的文件包中给出。
预定义类型可以用作为特定应用程序声明新类型的基础,也是本章的主题。
用户定义的类型
Example program ------> e_c07_p1.ada
-- Chapter 7 - Program 1 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure DerTypes is type LITTLE_INT is range -24..17; type TINY_INT is range -3..2; type POS_INT is range 25..38; type TINY_POS is new POS_INT range 25..30; type SALAD_INT is new INTEGER; type ANIMAL_INT is new INTEGER; type TREE_INT is new INTEGER range -557..1098; Salad : SALAD_INT; Lettuce : SALAD_INT := 22; Tomatoes : SALAD_INT := 14; Animals : ANIMAL_INT; Dogs : ANIMAL_INT := 3; Cats : ANIMAL_INT := 4; Trees : TREE_INT; Oak : TREE_INT := 12; Coconut : TREE_INT := 8; Count : INTEGER; begin Salad := Lettuce + Tomatoes; Animals := Dogs + Cats; Trees := Oak + Coconut + TREE_INT(Animals); Count := INTEGER(Trees) + INTEGER(Salad); Salad := SALAD_INT(Dogs) * Tomatoes + SALAD_INT(Cats) * SALAD_INT(Oak) + SALAD_INT(Count); Put("The 1st Salad calculation is "); Put(INTEGER(Salad)); New_Line; Salad := SALAD_INT(Dogs * ANIMAL_INT(Tomatoes) + Cats * ANIMAL_INT(Oak) + ANIMAL_INT(Count)); Put("The 2nd Salad calculation is "); Put(INTEGER(Salad)); New_Line; end DerTypes; -- Result of execution -- The 1st Salad calculation is 153 -- The 2nd Salad calculation is 153
检查名为e_c07_p1.ada的程序,查看几个用户定义类型的示例。第7行到第9行分别声明了一个全新的类型。仅考虑预定义的INTEGER类型和这三种类型,我们在程序中使用了四种类型,每种类型与其他三种类型无关。如果程序员没有明确说明正确的类型转换,这些类型的变量就不能以任何方式混合在一起。此外,不能为使用这些类型之一声明的变量分配超出其声明范围的值。声明用户定义的整数类类型的结构如下所示:
type <type-name> is range <lower-limit>..<upper-limit>;
如前所述,range是一个保留字,它的存在向编译器表明您希望拥有整数类的类型。
这三个新类型的用法在这个示例程序中没有说明,但是勤奋的学生可以很容易地使用这些新类型来声明一些变量,然后在一些数学语句中使用它们。
派生类型相对较新
派生类型是一个全新的主题,因为它们在您可能使用过的任何更流行的语言中都不可用。派生类型之所以得名,是因为它们是从现有类型派生的,而不是从全新的创建类型派生的。派生类型具有父类型可用的所有操作,但其范围可能比父类型的范围更有限。示例程序在第10行中对此进行了说明,其中类型TINY_POS是从用户定义的类型POS_INT派生而来的,但允许的范围稍窄。对POS_INT类型的变量执行的任何合法操作对TINY_POS类型的变量都是合法的。
派生类型的关键是使用保留字new以及派生新类型的类型。用于声明用户定义的派生类型的结构如下所示:
type <type-name> is new <existing-type>;
派生类型将与派生它的类型属于同一类。
我们继续在第11行到第13行中声明新类型,因为这三个类型都是全新的类型,每个类型都派生自父类型INTEGER,所以它们不能相加、相减、比较,甚至分配给其他类型,而不会带来额外的麻烦。第7行到第13行中的七个新类型中的每一个都共享为INTEGER定义的所有操作,包括算术、逻辑和赋值操作,但这些操作只能在对象的类型一致时执行。当然,可以通过显式类型转换来允许这些类型的变量组合。
在第13行中定义的类型TREE_INT是一个派生类型,其范围比父类型更为有限,因此它具有父类型的所有特征,但不能为其指定超出其定义范围的值。
如何使用一些新类型
作为使用新类型的一个示例,请考虑类型SALAD_INT,它是父类型INTEGER的派生类型。由于 Salad, Lettuce, Tomatoes,这三个变量都是同一类型的,因此它们可以*地添加、比较、分配给其他变量,或者以任何合法的方式用于整数类变量。它们可以用作for循环中的范围限制。这三个变量的范围与父类型整数相同,即在大多数32位计算机上为-2147483648到2147483647。类型为universal_integer的常量可以添加到这三个变量中,与之比较,或分配给这三个变量。
这些新类型对程序有什么帮助?
上一段中关于SALAD_INT 的所有内容都适用于ANIMAL_INT, TREE_INT或其他四种类型中的任何一种。假设在我们的程序的某个地方,我们试图把Tomatoes 的数量和Dogs的数量相加,然后把结果分配给 Trees。如果变量名是有意义的,我们可能不想在任何实际的程序中进行这样的操作。Ada编译器会给我们一个编译时错误,所以我们会在试图用这样一个愚蠢的语句运行程序之前检测到错误。仔细的类型分配可以用来保护我们不受那些我们都很容易犯的愚蠢的小错误的影响。让编译器找到这些愚蠢的小错误,让我们*地找到我们也会犯的分析错误,这样会更有效率。
如何进行类型转换?
即使您非常小心地进行了设置,也可能需要对数据执行一些操作,实际上需要将Animals的数量添加到Oak加Coconut的总数中。第30行说明了如何做到这一点。将变量括在括号中,并将所需类型添加到分组的前面,将使类型从变量的实际类型更改为括号前面的类型,但仅适用于程序中的这一位置。
在第31行中,Trees和Salad都被转换为INTEGER类型,然后被求和并分配给Count,一个INTEGER类型的变量。第33行说明了许多变量的加法,方法是首先将每个变量转换为SALAD_INT类型,然后执行加法。在第40行中,相同的变量被加在一起,但是在这种情况下,所有变量在求和之前被转换成ANIMAL_INT 类型,然后求和被转换成SALAD_INT类型。这两个方法的结果应该是相同的,您可以在编译和运行程序时验证它们是否正确。
什么是子类型?
子类型是基于父类型的新类型,但通常具有更严格的范围。它继承了父类型的所有特性,此外,它还可以在计算和赋值语句中与父类型*混合。使用子类型的原因通常是为了声明父类型的变量,但是要使用更有限的范围来利用Ada的范围检查功能。
我们也可以有派生类型的子类型
Example program ------> e_c07_p2.ada
-- Chapter 7 - Program 2 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure DerSubs is type NEW_INT is new INTEGER range 12..127; type NEW_INT_TYPE is new INTEGER; subtype SUB_INT is NEW_INT; subtype NEW_SUBTYPE is NEW_INT range 12..127; type DER_SUB is new NEW_SUBTYPE range 12..32; Arrow, Dart : NEW_INT; Size : NEW_INT_TYPE; Thing : INTEGER := 15; Point : NEW_SUBTYPE; begin Size := 10; Arrow := 23; Dart := 2 * Arrow - 25; Dart := Arrow + 2 * (NEW_INT(Size + 2) + NEW_INT(Thing)); Dart := Arrow + 2 * NEW_INT(Size + NEW_INT_TYPE(Thing)); Point := Arrow + Dart; end DerSubs; -- Result of execution -- (No output from this program.)
名为e_c07_p2.ada的程序给出了派生类型的子类型和子类型的派生类型的定义和使用示例。在第7行我们声明一个派生类型,在第10行我们声明一个新派生类型的子类型。派生类型的子类型与派生类型具有相同的特性,只是在本例中它的范围更为有限。类型NEW_SUBTYPE的变量与其父类型NEW_INT的变量兼容。如第26行所示。
在本例中,NEW_SUBTYPE与INTEGER类型不同,就像SALAD_INT在上一个程序中一样。
我们可以有一个子类型的派生类型
第12行说明了基于对父类型使用子类型的派生类型的声明。请注意,新的派生类型具有其父类型的所有特性(除了更受限制的范围),但就类型检查而言,它还是一个全新的类型。
子类型可以只是同义词
第10行说明了覆盖其父类型整个范围的子类型。由于此子类型的变量可以与其父类型的变量*混合,因此子类型名称只是父类型名称的同义词。
随着对上一个课程的讨论在你脑海中浮现,你应该轻松地读完本课程的其余部分。一定要编译并执行它。
对父级使用其他预定义类型
Example program ------> e_c07_p3.ada
-- Chapter 7 - Program 3 with Ada.Text_IO; use Ada.Text_IO; procedure MoreDers is -- Some floating point types type NEW_FLOAT1 is digits 5; type NEW_FLOAT2 is digits 5 range 1.0..12.0; type DER_FLOAT is new FLOAT; type LIM_FLOAT is new FLOAT range 0.0..555.5; subtype SUB_FLOAT is DER_FLOAT range -2.3..12.8; -- Some fixed point types type NEW_FIXED1 is delta 0.5 range 1.0..12.0; type NEW_FIXED2 is delta 0.05 range 1.0..12.0; type DER_FIXED is new NEW_FIXED1; type LIM_FIXED is new NEW_FIXED1 range 1.0..5.5; subtype SUB_FIXED is DER_FIXED range 2.1..2.8; -- Some CHARACTER types type DER_CHAR is new CHARACTER; type ALPHA_CHAR is new CHARACTER range 'A'..'Z'; subtype HEX_CHAR is ALPHA_CHAR range 'A'..'F'; -- Some enumerated types type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN); type WEEKDAY is new DAY range MON..FRI; subtype BOWLING_DAY is WEEKDAY range WED..THU; -- Some floating point objects Direction : FLOAT; Speed : DER_FLOAT := 100.0; Length : LIM_FLOAT := 72.41; Size : SUB_FLOAT := 4.3; begin Direction := 1.2 + FLOAT(Length + LIM_FLOAT(Speed * Size)); end MoreDers; -- Result of execution -- (There is no output from this program)
检查名为e_c07_p3.ada的程序,查看基于ada中其他预定义类型的派生类型和子类型的示例。我们首先在第8行和第9行声明两个用户定义的类型,这两个类型属于浮点类型,因为定义中出现了保留字digits。在第10行中,我们声明了 DER_FLOAT,它具有预定义类型FLOAT的所有特性,只是编译器会认为它是一个完全不同的类型,并且不允许这两种类型的混合。当然,必要时可以使用类型转换。
派生类型LIM_FLOAT声明有FLOAT的所有特征,除了它有一个允许编译器检查的有限范围之外。第12行包含子类型的定义,该子类型基于范围更为有限的DER_FLOAT。SUB_FLOAT类型的变量可以与DER_FLOAT类型的变量*混合,但不能与FLOAT、NEW_FLOAT1、NEW_FLOAT2或LIM_FLOAT类型声明的变量混合。
第15行到第19行说明了应用于定点类型的相同原则,应该是不言自明的。唯一的区别是在定点定义中使用保留字delta。
第22到29行说明了CHARACTER字符类型和枚举类型的派生类型和子类型的声明。这些语句将留给学生学习,因为它们与使用FLOAT作为父类型的示例非常相似。我们将在第11章研究CHARACTER 类型。
在第32行到第35行中声明了一些变量,初始化了一些变量,在第39行中给出了一个无意义的计算,以说明可以用派生类型进行的类型转换。
确保编译并执行这个程序,即使它没有输出。
在ADA中CLASS 是什么?
Ada中的类是从一个共同的祖先或共同的根继承下来的各种具体类型的整个组。根的名称用作类的名称。因此,在名为e_c07_p3.ada的程序中,NEW_FIXED1类由NEW_FIXED1、DER_FIXED、LIM_FIXED和SUB_FIXED类型组成,因为后三个类型以某种方式派生自NEW_FIXED1。以类似的方式,名为DAY的类由DAY、WEEKDAY和BOWLING_DAY类型组成。
关于类型的总结
我们已经看到,除了预定义的类型之外,我们还可以声明其他类型以在程序中使用。然后,我们可以使用任何预定义的或用户定义的类型作为子类型或派生类型的父类型。新的子类型或派生类型可以用作其他子类型或派生类型的父类型,并且我们发现在定义数据以解决任何特定问题时具有极大的灵活性。
编程练习
1.修改名为e_c07_p1.ada的程序以包含包的新实例Ada.Text_IO.Integer_IO输出第37行和第44行中名为Salad的变量,不进行类型转换。(Solution)
---------------------------------------------------------------------------------------------------------------------------
原英文版出处:https://perso.telecom-paristech.fr/pautet/Ada95/a95list.htm
翻译(百度):博客园 一个默默的 *** 的人