ADA 95教程 其他标量类型1
我们自己的类型
本教程中的大多数示例程序都使用了预定义的整数类型来说明各种概念,由于其多功能性,它是一个很好的选择。还有其他几种类型可用,因为它们是Ada定义的一部分,我们可以为特殊目的定义自己的类型。本章将说明这样做的一些原因。
下一章将给出类型的完整描述,但首先我们将学习如何使用其中的一些类型。我们陷入了一个进退两难的境地,就像谚语所说的“鸡和蛋哪个先来?”,并且必须首先选择要定义的。我们选择在本章中说明用法,然后在下一章中给出类型定义的细节。
几个整数类类型
Example program ------> e_c06_p1.ada
-- Chapter 6 - Program 1 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure AllInt is Data : INTEGER; Form : POSITIVE; Once : NATURAL; type MY_INTEGER is range -1000..24000; type MY_SHORT is range -12..127; subtype MY_SUBTYPE is MY_INTEGER range -12..127; Index : MY_INTEGER := 345; Stuff : MY_INTEGER := 33; Count : MY_SHORT := 54; begin Put("The type MY_SHORT covers the range of"); Data := INTEGER(MY_SHORT'FIRST); Put(Data); Put(" to"); Data := INTEGER(MY_SHORT'LAST); Put(Data); New_Line; Put("and its base covers the range of"); Data := INTEGER(MY_SHORT'BASE'FIRST); Put(Data); Put(" to"); Data := INTEGER(MY_SHORT'BASE'LAST); Put(Data); New_Line(2); Put("The type MY_INTEGER covers the range of"); Put(INTEGER(MY_INTEGER'FIRST)); Put(" to"); Put(INTEGER(MY_INTEGER'LAST)); New_Line; Put("and its base covers the range of"); Put(INTEGER(MY_INTEGER'BASE'FIRST)); Put(" to"); Put(INTEGER(MY_INTEGER'BASE'LAST)); New_Line(2); if Index in MY_SUBTYPE then Put_Line("Index is in the range of MY_SUBTYPE"); end if; if Index not in MY_SUBTYPE then Put_Line("Index is not in the range of MY_SUBTYPE"); end if; if Index in 12..377 then Put_Line("Index is in the range of 12..377"); end if; if Index not in Stuff..3 * (Stuff - 4) then Put_Line("Index is not in the range of Stuff..3 * (Stuff - 4)"); end if; end AllInt; -- Result of execution -- The type MY_SHORT covers the range of -12 to 127 -- and its base covers the range of -128 to 127 -- -- The type MY_INTEGER covers the range of -1000 to 24000 -- and its base covers the range of -32768 to 32767 -- -- Index is not in the range of MY_SUBTYPE -- Index is in the range of 12..377 -- Index is not in the range of Stuff..3 * (Stuff - 4)
检查名为e_c06_p1.ada的文件,以获得一些示例,说明我们如何以及为什么使用自己的类型定义。第7行到第9行中所示的三种类型可用于所有Ada编译器,因为它们用途广泛且有用,而且是Ada编程语言所必需的。如前所述,INTEGER类型定义了一个变量,在大多数32位计算机上,该变量可以具有-2147483648到2147483647之间的任何整数值。应该指出的是,有些微型计算机使用的标准量程要小得多。在大多数32位计算机上,类型NATURAL定义了一个从0到2147483647的变量,类型POSITIVE涵盖了从1到2147483647的范围。
考虑一下最后一段中的“most”这个词,想想如果你编写了一个依赖于覆盖所列范围的特定变量的程序,并试图将该程序移动到使用不同范围的另一台机器上,可能会遇到的问题。为了让程序在新电脑上运行,你可能会面临一个很大的重写问题。
我们如何帮助解决可移植性问题
假设我们将类型定义为覆盖某个范围,如本示例程序的第11行所示,并将程序移动到另一台计算机。根据Ada的定义,新的编译器有义务为我们创建一个覆盖给定范围的类型,或者给我们一个编译时错误,告诉我们硬件根本无法支持定义范围。在这种情况下,由于请求的范围很小,任何有意义的编译器和机器组合都能够覆盖定义的范围,并且我们将有一个程序可以运行,尽管标准类型的定义方式不同。良好的编程实践,尤其是如果源代码可能需要移到其他计算机上,将显式定义所有范围,并避免编译器内置的实现定义的限制。
第11行和第12行定义了两个新类型,程序使用一些新属性来说明这些新类型。在第21行和第24行中,我们使用了之前使用过的两个属性,但在第28行和第31行中,我们使用了两个新属性。为了让系统创建一个覆盖范围从-1000到24000的类型,它必须使用一个具有足够二进制位的结构来覆盖给定的范围。这个范围不是由二进制限制组成的,所以系统必须定义足够的位来覆盖这个范围和更多的位。它可能会定义一些8位字节的数量,而完整模式所覆盖的范围(如定义的那样)称为基本范围。这两个新属性给出了系统选择的基的限制。基本限制可能是-32768到32767,即使您使用的是32位系统,也可能是-128到127。
你有智能编译器吗?
这里所示的类型MY_SHORT定义了-12和127的极限,这是一个相对较小的范围。它足够小,可以容纳在-128到127的基本范围内,可以存储在一个8位字节中。如果您的编译器足够聪明,能够实现这一点,那么它可以使用一个8位字节来存储这种类型的每个变量,如果您有很多这样的变量要存储,那么它将为您节省大量内存。但是,您可能会发现,大多数编译器只会使用完整的整数范围作为基类型,即使是这个小数字。监视器上会显示两种不同类型的四个属性,供您参考。从运行这个程序的结果可以看出编译器是如何存储这两种类型的。
in和not in运算符
我们现在要学习一个新的操作符,第46行所示的in操作符。如果变量索引(由于初始化而具有345的当前值)在子类型MY_SUBTYPE的定义范围内,则返回布尔类型TRUE,否则返回布尔类型FALSE。这个结果可以分配给一个布尔变量或用于布尔决策,如图所示。在这种情况下,Index的值不在MY_SUBTYPE的范围内,因此将返回FALSE,并且不会输出消息。第50行说明了另一个操作,它是不在操作中的,应该是不言自明的。您应该能够看到将显示第51行中的消息。第54行和第58行进一步说明了in和not in运算符,其中显式范围用于测试范围。
一定要编译并运行这个程序并观察输出。这里是一个机会,让你看看你是否有一个智能编译器。如果您检查名为Interfaces的包,您会发现有一些其他类型可用,例如INTEGER_8、INTEGER_16和INTEGER_32。这些都是可选的,但有些肯定可以在编译器中使用。
枚举类型
Example program ------> e_c06_p2.ada
-- Chapter 6 - Program 2 with Ada.Text_IO; use Ada.Text_IO; procedure Enum is type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN); subtype WORK_DAY is DAY range MON..FRI; subtype PLAY_DAY is DAY range SAT..SUN; type HEAVENLY_BODY is (MOON, SUN, EARTH, MARS); Big_Sphere : HEAVENLY_BODY; package Day_IO is new Ada.Text_IO.Enumeration_IO(DAY); use Day_IO; package Body_IO is new Ada.Text_IO.Enumeration_IO(HEAVENLY_BODY); use Body_IO; Day_Of_Week : DAY; Today : DAY; Happy_Day : PLAY_DAY; Bowling_Day : DAY range THU..SAT; Index : INTEGER; begin Day_Of_Week := WED; -- WED Day_Of_Week := DAY'FIRST; -- MON Day_Of_Week := DAY'LAST; -- SUN Day_Of_Week := DAY'PRED(Day_Of_Week); -- SAT Day_Of_Week := DAY'SUCC(PLAY_DAY'FIRST); -- SUN Index := DAY'POS(MON); -- 0 Index := DAY'POS(WED); -- 2 Day_Of_Week := DAY'VAL(1); -- TUE for Day_Of_Week in WORK_DAY loop Put("We are in the workday loop"); New_Line; end loop; Today := THU; if Today <= WED then Put("Early in the week"); New_Line; end if; if Today >= WED then Put("Late in the week"); New_Line; end if; Today := SUN; Big_Sphere := SUN; Today := DAY'(SUN); Big_Sphere := HEAVENLY_BODY'(SUN); Put(Today); Put(DAY'PRED(Today)); Put_Line(" from type DAY."); Put(Big_Sphere); Put(HEAVENLY_BODY'PRED(Big_Sphere)); Put_Line(" from type HEAVENLY_BODY"); end Enum; -- Result of execution -- We are in the workday loop -- We are in the workday loop -- We are in the workday loop -- We are in the workday loop -- We are in the workday loop -- Late in the week -- SUNSAT from type DAY. -- SUNMOON from type HEAVENLY_BODY
检查名为e_c06_p2.ada的程序,让我们第一次了解枚举类型及其在程序中的用法。第7行是枚举类型的第一个定义,使用保留字类型,如图所示。类型名是在两个保留字之间给出的,允许此类型的变量分配给它的值是以括号内的列表形式给出的。这些值实际上表示从0到最大值所需的值(在本例中为6)之间的数值,因为编号将从0分配到6。在第20行中,名为Day_Of _Week的变量被声明为Day类型,因此可以为其指定Day类型的7个值中的任何一个,而不指定其他值。我们可以赋值0,1,2,..6表示一周中的7天,并在程序中使用数值,但通过使用枚举类型,我们可以将星期日称为SUN,星期一称为MON等,使程序更清晰,更容易理解。
Ada语言中有三种预定义的枚举类型:布尔型、字符型和宽字符型。BOOLEAN类型是一个枚举类型,有两个可能的值,但是它有一些特殊的属性,没有其他枚举变量。这些将使用下一个示例程序进行讨论。
跳转到当前示例程序中的可执行代码,我们在第28行演示赋值,其中我们将WED的值赋值给变量Day_of_Week。第29行和第30行展示了我们之前看到的整数类型变量的第一个和最后一个属性。正如-2147483648是可分配给32位整数类型变量的最低可能值一样,MON也是可分配给DAY类型变量的最低值,因此也是第一个值。
两个新属性
第31行和第32行说明了PRED(表示前置)和SUCC(表示后继)属性。PRED返回用作参数的变量的当前值的前一个值。由于在第30行中,为周的可变日分配了SUN的值,而在SUN的前一天是SAT,因此在第31行中,SAT被分配给周的可变日。尝试获取包含可用列表中第一个值的变量的PRED是错误的,并将导致引发异常范围错误Rang_Error。同样,尝试获取任何处于其最大值的变量的SUCC将导致引发异常范围\错误Rang_Error。本教程稍后将详细介绍例外情况。此时只需记住,异常是指异常情况或错误。
什么是枚举类型的子类型?
在第8行和第9行中,我们定义了类型DAY的两个子类型,它们将具有类型DAY的所有特征,但范围更为有限。声明为PLAY_DAY类型的变量可以指定两个值,SAT或SUN。SAT的数值为5,SUN的数值为6,这两个值都是从父类型DAY继承的。因此,在第32行中,我们首先使用属性来获取PLAY_day类型的第一天,它将是SAT,然后使用属性SUCC来获取该值的后续值,它将是SUN。注意如何组合属性以获得所需的信息。子类型的赋值与其父类型兼容。我们将在本教程的下一章更详细地讨论子类型。
现在是POS和VAL属性
POS属性将返回一个universal_integer类型的值,该值表示括号中枚举值的位置,如第33行和第34行所示。VAL属性将返回括号中包含的数值的枚举值。请注意,如果第35行中的类型DAY更改为PLAY_DAY,则会返回一个错误,因为这是该类型的非法枚举值。该错误将通过引发异常范围返回。
枚举分配呢?
Happy_Day的值可以随时分配给Day_of_Week,因为它们是类型兼容的,并且任何可以合法分配给Happy_Day的值也可以分配给Day_of_Week。但是,不能总是将Day_of_Week指定给Happy_Day,因为允许Day_of_Week包含一些不合法的值来指定Happy_Day。这说明在使用枚举类型时必须小心,但是如果使用得当,它可以通过使用Ada语言中定义的强类型检查来帮助程序调试。
使用枚举类型进行控制
第37行到第40行中的循环正好覆盖了子类型WORK_DAY所覆盖的范围,因此我们可以在循环定义的范围部分使用它。当您运行这个程序时,您将看到循环将被执行五次。
第42行到第51行包含对变量的两个关系检查,以说明枚举类型变量可以在布尔表达式中使用。所有布尔运算符都可用,包括以下列表,没有其他运算符;
= equality /= inequality > greater than >= greater than or equal to < less than <= less than or equal to
枚举类型变量没有可用的数学运算。如本示例程序所示。
什么是资格? --(这个不太对)
在第53行和第54行中,我们将相同的值赋给两个不同的枚举类型变量。至少看起来是一样的。实际上,它们是两个同名的不同值,即SUN。因为Ada做了如此强大的类型检查,所以它很聪明地意识到它们实际上是两个不同的常量,它将根据要分配给它的变量的类型为每个语句选择一个它需要的常量。
第56行和第57行通过限定您感兴趣的值来进行相同的赋值,但在这种情况下,这些限定是不必要的。可能有一种情况,你需要告诉系统你感兴趣的SUN的哪个值。限定使用枚举值之前后跟“勾号”或撇号的类型。
---------------------------------------------------------------------------------------------------------------------------
原英文版出处:https://perso.telecom-paristech.fr/pautet/Ada95/a95list.htm
翻译(百度):博客园 一个默默的 *** 的人