创建Ada数据类型和子类型
创建数据类型和子类型(Creating Types and Subtypes)
使用变量时,除了以某标识符作为变量的名称外,还要指定该变量的数据类型。一个数据类型定义了变量可接受的值以及所能执行的操作。比如说,一个数据类型为 Age 的变量 Bill,Age 的取值范围为 1..100,并只有 + – 这两种操作,在这里,对象(object)为名为 Bill 的变量,它的取值在 1..100 之间(包括 1,100),值的变化只能通过+ -这些基本运算符(primitive operation)来实现,而无法通过* /等其它运算符。Ada 中的数据类型,包括预定义类型,都是按照一定的格式在程序包中创建的。下面就介绍创建数据类型的一些基本内容,更多相关内容会在以后见到。
创建新的数据类型
创建一个新类型,需要使用保留字 type,is,range。格式如下:
type type_name is range range_specifcation;
type_name 为新类型的名称,是一个合法标识符;range_specifcation 表示该类型的取值范围,表示方式为 First .. Last,如 1..100 , -9 ..10 。
例如创建上面提及的一个新类型 Age :
type Age is range 1 .. 100;
这样就有了一个数据类型 Age, 取值范围 1 .. 100。
有一点要注意:range_specfication 中 First 要大于 Last。 如 type months is range12 .. 0, 实际上 months 是一个空集(null),而不是所期望的 0..12。
不同数据类型之间是不能进行混合运算的,即使取值范围和运算符一样,看以下的程序例子:
000 — putwage.adb
001 with Ada.Text_IO; use Ada.Text_IO;
002 with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
003 procedure putwage is
004 subtype Age is Integer range 1 .. 100;
005 subtype Wage is Integer;
006 Bill_Age : Age := 56;
007 Bill_Wage: Wage := 56;
008 begin 009 Put (―Total wage is‖);
009 Put (Bill_Wage * Bill_Age);
010 New_Line;
011 end putwage;
[001]-[002]: 使用软件包 Ada.Text_IO,Ada.Integer_Text_IO;两个软件包分别处理字符类输出和整数输出。
[003] [008] [012] 定义一个过程 putwage。 [004]-[005]: 定义新的数据类型Age,Wage,它们取值范围都为 1..100。
[006]-[007]: 声明两个变量 Bill_Age,Bill_Wage,类型分别为 Age 和Wage, 并赋予相同初始值56。
[009]-[011]:依次输出字符串‖Total wage is‖,整数 Bill_Wage和Bill_Age的乘积,和一个新行符(EOL)。
以上程序看上去毫无问题,但根本无法编译通过。首先,没有定义类型Age和wage的 * 操作,因此Bill_Age和Bill_Wage无法相乘;第二,两者数据类型不同,即使定义了*操作,还是无法相乘。 当然也可使用后面提到的类型转换 ,如果将[010]改为Put (Integer(Bill_wage) * Integer(Bill_Age)),将会输出所要的 3136;但如果改成Put (Integer(Bill_wage * 56)),看上去也行的通,但实际结果却不是3136。不同数据之间不能进行运算,要牢牢记住。(Integer 是预先定义的一个整型,Integer(Bill_Wage)是将Bill_Wage强制转换为整型)。
派生类型
大家可能会发现,如果像上面一样创建一个截然不同的新类型,还需要定义它的运算符,使用很不方便。因此,往往是派生现有的类型,其格式为:
type type_name is new old_type {range range_specification};
type_name 为新类型的名称,是一个合法标识符;range range_specification 表示该类型的取值范围,是可选的,没有的话表示新类型 type_name 的取值范围和 old_type 一样。如将上例改为:
000 — filename:putwage.adb
001 with Ada.Text_IO; use Ada.Text_IO;
002 with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
003 procedure putwage is
004 type Age is new Integer range 1 .. 100;
005 type wage is new Integer;
006 Bill_Age : Age := 56;
007 Bill_Wage: Wage := 56;
008 begin
009 Put (―Total wage is‖);
010 Put (Bill_Wage * Bill_Age);
011 New_Line;
012 end putwage;
上例还是不能编译通过,因为派生类型只继承母类型的属性,如运算符,不同的派生类型即使母类型相同也还是属于不相同的类型。但将[10]改为Put (Integer(Bill_wage * 56))则能输出正确的结果。但是派生类型使用还是麻烦了一点,不同类型之间即使都是数字类型也无法混合使用,只是自己不用创建运算符省力了点。
创建子类型
创建新类型和派生类型的麻烦从上文就可以感受的到,特别是在科学计算这些有很多种小类型的软件当中,上述两种方法实在过于繁杂。这时子类型(subtype)就相当有用,子类型的定义格式为:
subtype type_name is old_type {range range_specification};
type_name 为新类型的名称,是一个合法标识符;range range_specification 表示该类型的取值范围,是可选的,没有的话表示新类型 type_name 的取值范围和 old_type 一样。
再将先前的例子改一下:
000 — putwage.adb
001 with Ada.Text_IO; use Ada.Text_IO;
002 with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
003 procedure putwage is
004 subtype Age is Integer range 1 .. 100;
005 subtype Wage is Integer;
006 Bill_Age : Age := 56;
007 Bill_Wage: Wage := 56;
008 begin 009 Put (―Total wage is‖);
009 Put (Bill_Wage * Bill_Age);
010 New_Line;
011 end putwage;
编译通过,输出值为3136。子类型不仅继承母类型的属性,而且和母类型、其它同母类型的子类型可混合使用。
在前面的例子中的,我们都提到了取值范围,这也是 Ada 的一项―特色‖:Ada 不同于 C 和 Pascal— 赋给一个变量超过其取值范围的值或进行不合法运算,会输出错误的值而不报错,与此相反,Ada 程序在编译时会提示错误,或在运行 Ada 程序时产生Constraint_Error异常(异常和 C 中的信号Signal差不多。