2.5 Ada语言数据类型结构
Ada语言数据类型结构在很大程度上是以Pascal类型结构为基础的,但Ada类型结构更丰富、更系统。在这一节将讨论它的主要数据类型,在其他章节还会涉及Ada的一些数据类型。
2.5.1 标量类型
标量类型(Scalar Type)定义非结构数据对象,可分为数值类型(Numeric Type)和用户自定义的枚举类型(Enumeration Type)两种。数值类型可进一步分为整型和实型。整型和枚举类型也称为离散类型(Discrete Type)。任何标量类型的值的集合都是有序的,所以关系运算符对它们都有定义。
所有的整型都是由一个有序的整数值的集合组成的。Ada预定义一个称为INTEGER的整型,还预定义了短整型SHORT- INTEGER和长整型LONG- INTEGER。预定义INTEGER的值域取决于实现。例如,在16位机器上,它的值域为-32768~32767。一个给定的Ada实现必须支持INTEGER数据类型,可以支持也可以不支持短整型和长整型。
程序员可以通过range结构来定义其他的整型,例如
type TWO-DIGIT is range 0..99;
其中,保留字range后面跟着整型域的下界和上界值,它们是可静态计算的任意表达式(常数或常数表达式)。
Ada为实数值精心设计了一组丰富而又灵巧的定义方法,这里只讨论其中的主要部分,实型定义实数的近似值的集合。由于一个给定实现只能对数值数据提供有限的二进位表示,因而不可能对所有实数都提供精确的表示,实际上只能表示数学上实数的一个子集。
Ada预定义一个称为FLOAT的实型,还预定义短实型SHORT-FLOAT和长实型LONG-FLOAT。与整型一样,它们的精度依赖于实现。程序员可以定义自己的实型,例如
type FLOAT-1 is digits 10;
FLOAT-1和FLOAT一样,都是浮点实型。保留字digits后面跟着十进制有效位数的静态整型表达式,以描述浮点数的相对精度。其后还可跟上范围结构range,例如
type MASS-READING is digits 7 range 0.0..3.0;
其范围的上、下界必须取实数值,即可静态计算的任意实型表达式。
说明FLOAT-1中的数字10规定十进制实数的最小有效位数,而FLOAT的最小有效位数是预定义的。Ada定义了一个属性DIGITS,它给出相应实型数值的最小有效位数。若T是浮点实型,那么T'DIGITS就是类型T的最小有效位数。因此,T'DIGITS规定类型T的实数近似表示的出错界限。
定点表示是另一种大家熟知的用来逼近实数值的技术。这种说明形式是用保留字delta后面跟着定义此类型的增量,而增量是静态实型表达式,例如
type FIX-PT is delta 0.01 range 0.00..100.01;
其中,range后面跟着范围说明,它应该是静态实型表达式,与浮点表示不一样,它是不可省略的。
Ada的枚举类型与Pascal类似,例如
type DAY is (SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY, FRIDAY,SATURDAY);
Ada提供的字符型和布尔型是枚举类型的特例,例如
type CHARACTER is(the ASCII character set); type BOOLEAN is(FALSE,TRUE);
2.5.2 组合类型
Ada组合(或结构)类型分为数组和记录。
1.数组
Ada数组与Pascal数组一样,具有确定的下标界,例如
type MONTH is (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT, NOV,DEC); type YEARLY-PAY is array(MONTH)of INTEGER; type SUMMER-PAY is array(MONTH range JUL..SEP) of INTEGER;
其中,YEARLY-PAY和SUMMER-PAY称为约束数组类型(Constrained Array Type),因为它的下标界是静态确定的。
Ada的数组与Pascal的数组还有些不同,它支持动态数组,例如说明
type SOME-PERIOD-PAY is array(MONTH range<>)of INTEGER; type INT-VECTOR is array(INTEGER range<>)of INTEGER; type BOOL-MATRIX is array (INTEGER range <>,INTEGER range <>)of BOOLEAN;
其中,符号“<>”称为框(Box),表示范围未说明,SOME-PERIOD-PAY,INT-VECTOR和BOOL-MATRIX称为非约束数组类型(Unconstrained Array Type)。
Ada数组类型由分量的类型、下标个数和下标类型来刻画,界的值不作为数组类型的一部分来考虑,因在编译时界的值尚未确定。在上例中,数组SOME-PERIOD-PAY的下标具有类型MONTH,数组INT- VECTOR和BOOL- MATRIX的下标具有类型INTEGER。“range<>”子句说明实际的界,它留下来未被说明,待数据对象成为实体时,界的值才能确定。例如在说明
SPRING-MONTH:SOME-PERIOD-PAY (APR..JUN); Z:INT-VECTOR(-100..100); W:INT-VECTOR(20..40); Y:BOOL-MATRIX(0..N,0..M);
中,界的值不必用静态表达式给出,只要求处理对象说明时能最后确定即可。
界的实例化(即界的确定)还可通过参数传递完成。例如,下列函数接受类型为INT-VECTOR的对象,并累加它的分量。
function SUM(X:INT-VECTOR)return INTEGER; RESULT:INTEGER:=0; begin for I in X'FIRST..X'LAST loop RESULT:=RESULT+X(I); end loop; return RESULT; end SUM;
其中,变量RESULT局部于SUM,当它被说明时赋初值0;循环变量隐式说明;形参X自动与实参具有相同的界,这些界可由数组名连接属性名FIRST和LAST来访问。因此,可用不同
大小的数组作为实参来调用该函数。例如
A:=SUM(Z)+SUM(W);
也可以在过程的局部说明中说明一个数组,它的界依赖于一个参数。例如
TEMPORARY:INT-VECTOR(X'FIRST..X'LAST);
Ada的串看成字符数组,STRING的预定义为
type STRING is array(POSITIVE range<>)of CHARACTER;
其中,POSITIVE是整型子界1..INTEGER'LAST。
除了传统的通过下标选取数组分量的方法外,Ada还提供一个选取一维数组若干相继分量的方法,这种方法称为切片。它对串操作特别有意义,因为它支持子串的概念。例如,下列语句定义变量LINE为80个字符的串,并把一个一维字符数组赋予LINE的一个切片。
LINE:STRING(1..80); …… LINE(1..11):=('D','e','a','r',',','f','r',''i,'e','n','d');
2.记录
Ada的记录类似于Pascal的记录,它们都支持笛卡儿积和判定或的构造方法。例如
type COORDINATE is record X:INTEGER range 0..100; Y:CHARACTER; end record;
是一个笛卡儿积。我们曾经指出,原始Pascal定义中所支持的判定或带来不安全的问题,现在来考虑Ada是如何处理判定或的,以及它如何解决不安全问题。在2.4.2节中对有关变体记录的讨论中,曾写出了一个Pascal说明,这个说明在Ada中可以写成
type DEPT is(HOUSEWARE,SPORTS,DRUGS,FOOD,LIQUOR); type MONTH is range 1..12; type ITEM(AVAILABLE:BOOLEAN:=TRUE)is record PRICE:REAL; case AVAILABLE of when TRUE=>AMOUNT:INTEGER; WHERE:DEPT; when FALSE=>MONTH-EXPECTED:MONTH end case; end record;
其中,类型ITEM具有一个判定式AVAILABLE,它定义ITEM可能的变体。若ITEM的变体说明默认判定式的值,那么,在上述说明中已对这个默认值赋了初值TRUE,因此对象可以说明成
PEACH:ITEM;
也可以说明一个对象的变体是冻结的,例如
ORANGE:ITEM(FALSE);
在这种情况下,编译器为ORANGE保留的存储区恰好是FALSE这个变体所需要的,运行时,变量不能改变为其他的变体。
Ada这种处理判定或的变量是安全的。事实上,判定式是强制性的,不能对它直接赋值。若判定式无默认初值,那么,任何对象说明中都必须对判定式加以约束(Constraint)。
判定式的值只有在对象没有给出显式约束的情况下才能改变,例如PEACH可以改变而ORANGE不能改变判定式的值。然而,只有把记录作为一个整体时,判定式的值才能改变,单独对判定式赋值是不允许的。例如说明
COCA-COLA:ITEM;
其中,变量COCA-COLA具有默认初值TRUE。若在上述说明之后,再加语句
COCA-COLA:=ORANGE;
使变量得到ORANGE的值,即可建立FALSE变体。我们也可以给COCA-COLA赋一个记录值,例如
COCA-COLA:=(PRICE=>1.99,AVAILABLE=>TRUE,AMOUNT =>1500,WHERE=>FOOD);
其中,赋值号的右边是一个记录值,由编译器自动转换成运行时测试。
if not COCA-COLA.AVAILABLE then raise CONSTRAINT-ERROR end if;
若COCA-COLA.AVAILABLE不为TRUE,则产生一个错误。
3.访问类型
Pascal的指针类型在Ada中称为访问类型(Access Type),用于动态分配和释放数据。例如说明
type BINARY-TREE-NODE; type TREE-REF is access BINARY-TREE-NODE; type BINARY-TREE-NODE is record INFO:CHARACTER; LEFT,RIGHT:TREE-REF; end
定义了一个二叉树,其中第一个语句有点陌生,它是所谓不完全类型说明,Ada的递归类型需要不完全类型说明。
若P和Q是类型TREE-REF的两个指针,那么由P指向的结点INFO成分是P.INFO,结点本身是P.all。因此,P指向的结点对Q指向的结点赋值可写成
Q.all:=P.all;
4.子类型和派生类型
Ada详细地区分了类型的静态特性和动态特性。静态特性在编译时检查,动态特性在运行时检查。类型的静态特性可用于操作,而动态特性仅仅是某种约束,例如对整数范围的约束、对数组下标的约束等。为了使得这种约束清楚起见,Ada允许程序员通过定义子类型(Subtype)来规定类型的一个特性,例如
type FLAVOR is (CHOCOLATE,MINT,PEACH,STRAWBERRY,VANILLA, BLUECHEESE,CATSUP,GARLIC,ONION); subtype ICE-CREAM-FLAVOR is FLAVOR range CHOCOLATE..VANILLA; subtype SMALL-INT is INTEGER range-10..10; subtype SMALL-POS-INT range 1..10; subtype MY-INT-SET is INTEGER range A..B;
其子类型继承基类型的所有特性,但它的值还要满足某个约束。例如,ICE-CREAM-FLA-VOR继承了FLAVOR的所有特性,但它还被约束于子集CHOCOLATE到VANILLA。子集MY-INT-SET表明约束可以包括表达式。但是,表达式不能静态求值,只有在说明出现的作用域入口详细说明子类型时,才能计算表达式的值。
子类型机制也可用来约束数组类型,例如
type MY-ORDERS is array(INTEGER range<>)of ICE-CREAM-FLAVOR; subtype MONTHLY-ORDERS is MY-ORDERS(1..31); subtype ANNUAL-ORDERS is MY-ORDERS(1..365);
其中,类型MONTHLY-ORDERS和ANNUAL-ORDERS的对象进一步说明产生的数组,相应的界值是1..31和1..365。
也可以使用子类型机制来冻结判定或类型的变量,例如
subtype OUT-OF-STOCK is ITEM(FALSE);
其中,ITEM是上面定义的变体记录。类型OUT-OF-STOCK的任何变量都规定AVAILA-BLE是FLASE而被冻结的。
最后,子类型也允许程序员用来绑定某个给定类型,这样的绑定可以是静态的,也可以是动态的。子类型机制并未定义新类型,只是简单地在值的集合上对一个对象采取某些限制。
Ada的派生类型(Derived Type)与子类型不同,它定义新类型。派生类型说明的一般形式可以用扩充BNF(参见9.3.2节)来定义,格式如下:
type<新类型>is new<母体类型>[<约束>];
其中,方括号内是可选择部分。例如说明
type POSITIVE is 1..INTEGER'LAST; type WEIGHT is new POSITIVE range 1..100; type LENGTH is new POSITIVE;
这些新类型继承了母体类型(Parent Type)的所有特性(值、操作和其他属性),但它们被看作与母体类型不同的类型。
5.属性
Ada的属性用来指定数据对象、类型的特性。一个属性的值是用一个实体名后面跟着一个“'”及属性名来表示的,例如,2.5.1节中的T'DIGITS。Ada预定义了40多个属性,另外还允许提供与实现有关的属性。
属性是程序设计的有力工具,用于支持规范的程序设计实践。例如,数组属性FIRST和LAST,允许程序员写出任意大小的数组间操作的子程序,也允许程序员用语句来显示选取不同的候选项,以便响应某些属性的值。
Ada语言数据类型结构如图2-5所示。
图2-5 Ada语言的数据类型结构