程序设计语言与编译
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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语言的数据类型结构