记录则是由命名分量(named component)组成的复合类型,即具有不同属性的数据对象的集合,和C 下的结构(structure)、Pascal 下的记录(record) 类似。Ada 的记录比它们提供的功能更强,也就是限制更少。同时记录扩展(record extension)是 Ada95 中类型扩展(继承)机制的基础,使记录的地位更加突出,关于记录扩展详见 第6章 面向对象特性,为了避免重复,本章对此不作介绍。
record_name
如下面的例子:
type
Id_Card
is
record
Full_Name : String (1..15);
ID_Number : Positive;
Age : Positive;
Birthday : String (1..15);
Familiy_Address : String (1..15);
Family_telephone : Positive;
Job : String(1..10);
end record;
My_Card :Id_Card;
一个简单ID卡的记录,包含Full_Name,ID_Number,Age,Birthday,Familiy_Address,Family_telephone,Job 这些成员。
4.3 访问和设置记录(Access and Set Records)
使用记录的成员时,只需在记录和其成员之间用 “.” 隔开即可。如赋予My_Card中的变量 Full_Name 值 Jack Werlch:
My_Card.Full_Name := "Jack Welch ";
设置记录成员的值和设置数组给人感觉上有点类似,如:
My_Card := ("Jack Welch ", 19830519,45, "Jan 1st 1976 ",
"China ",8127271,"CEO ");
将 ( )中的值依次赋给My_Card 的成员。
相同的数据类型的成员一多,无疑会使人不大明了,因此也可以:
My_Card := ( Full_Name => "Jack Welch ",
ID_Number => 19830519,
Age => 45,
Birthday => "Jan 1st 1976 ",
Familiy_Address => "China ",
Family_telephone => 8127271;
Job => "CEO ") ;
上面两种表示法可以混用,但按位值在有名的值前面:
My_Card := ( "Jack Welch ",
19830519,
Age => 45,
Birthday => "Jan 1st 1976 ",
Familiy_Address => "China ",
Family_telephone => 8127271;
Job => "CEO ");
但如果为:
My_Card := ( Full_Name => "Jack Welch ",
ID_Number => 19830519,
Age => 45,
Birthday => "Jan 1st 1976 ",
Familiy_Address => "China ",
8127271;
"CEO ");
则是非法的。
如果几个相同类型的成员,赋予同一数值,也可以:
My_Card := ( Full_Name => "Jack Welch ",
ID_Number | Family_telephone => 19830519,
Age => 45,
Birthday => "Jan 1st 1976 ",
Familiy_Address => "China ",
Job => "CEO ");
上例我们假设 ID_Number 和 Family_telephone 值是一样的,为19830519,不同成员间用 | 隔开。
记录类型有时在声明也需要默认值:
type
Id_Card
is
record
Full_Name : String (1..100) := "Jack Welch ",
ID_Number : Positive := 19830519,
Age : Positive := 45,
Birthday: String (1..20) := "Jan 1st 1976 ",
Familiy_Address :String (1..100):= "China ",
Family_telephone :Positive := 8127271;
Job : String(1..10) := "CEO ");
end record;
My_Card :Id_Card;
将 Jack Welch 的资料当作了 Id_Card 类型的默认值,My_Card 无须赋值,在声明时已经有了前几个例子中所赋的值。
声明常量记录如下:
My_Card :
constant Id_Card := ( Full_Name => "Jack Welch ",
ID_Number => 19830519,
Age => 45,
Birthday => "Jan 1st 1976 ",
Familiy_Address => "China ",
Family_telephone => 8127271;
Job => "CEO ";)
和创建其它类型的常量类似,只需在该记录类型前添个
constant。
4.4 变体记录 (Variant Record)
在讲变体记录前,先介绍一下记录判别式(record discriminant)的概念。判别式(discriminant)以前没接触过,这里先简单提一下它的定义:一个复合类型(除了数组)可以拥有判别式,它用来确定该类型的参数(具体参见 RM95 3.7 Discriminant)。也就是说,一个复合类型创建时可以有一些参数,在接下去声明该类型的变量时,可以通过那些参数的值来改变变量初始化时所占用内存大小、成员数量、取值等等。这一节以及下一节的无约束记录(unconstrained record)的内容都在记录判别式的范围内,至于其它复合类型将在以后讲述。
变体记录,即它的一些成员存在与否取决于该记录的参数。如我们将 Id_Card 这个记录类型扩充一下:
type
Id_Card (Age : Positive := 1)
is
record
Full_Name : String(1..15);
ID_Number : Positive;
Birthday : String(1..15);
Familiy_Address : String(1..15);
Family_telephone : Positive;
Job : String(1..10);
case Age is
when 1 .. 18 => School_Address : String(1..15);
when 19 .. 60 => Monthly_Income : Integer
;
Working_Address: String(1..15);
when others =>
null; -- 如果 Age 的值不属于 1..60,成员不改变
end case;
end record;
My_Card : Id_Card ;
Your_Card: Id_Card (Age => 20);
上例中,
case Age ...
end case 是变体部份,当 Age 值在 1..18 时,动态创建成员 School_Address;当 Age 值在 19..60 时,动态创建成员 Monthly_Income,Working_Address;当 Age 不在 1..60 时,数据成员不改动。在声明判别式时一般应赋予一个默认值,如上例 Age 的默认值为 1 ,这样声明变量 My_Card 时不带参数也可以,默认参数为 1。但如果 Age 没默认值,上例中的 My_Card 声明是非法的。
因此,记录 My_Card 有 Full_Name,ID_Number,Birthday,Familiy_Address,Family_telephone, Job,School_Address这些成员,因为 Age 默认为 1; 记录 Your_Card 中 Age 值为 20,因此有 Full_Name,ID_Number,Birthday,Familiy_Address,Family_telephone, Job, Monthly_Income,Working_Address 这些成员。
最后注意一下,变体部份要在记录类型声明的底部,不能在 Job 或其他成员前面---变体记录的变量大小是不定的。
4.5 无约束记录(Unconstrained Record) 上面的记录都是受限定的,如 创建 My_Card 后,它的判别式无法再被改动,Monthly_Income,Working_Address这些成员也无法拥有。但如果 ID_Card 的判别式有了初使值,则还有办法使记录动态改变。
如:
type Id_Card (Age : Positive := 1)
is
record
Full_Name : String(1..15);
ID_Number : Positive;
Birthday : String(1..15);
Familiy_Address : String(1..15);
Family_telephone : Positive;
Job : String(1..10);
case Age is
when 1 .. 18 => School_Address : String(1..15);
when 19 .. 60 => Monthly_Income : Integer
;
Working_Address: String(1..15);
when others =>
null;
end case;
end record;
My_Card : Id_Card ;-- 以上和上一节的例子一样
....
begin
...
My_Card := (17, "Jack Welch ", 19830519, "Jan 1st 1976 ", "China ",8127271,
"CEO ","Shanghai ");
end
赋值的时候就有了点特殊。My_Card 在程序内部赋值,但与常规赋值不同,它的第一个值是判别式的值,后面才是成员的值---成员数量按照判别式的值动态改变,上例就多了一个 School_Address 成员。这种情况下,编译器会分配给 My_Card 可能使用的最大内存空间。因此将下句接在上句后面:
My_Card := (17, "Jack Welch ", 19830519, "Jan 1st 1976 ", "China ",8127271,
"CEO ", 78112 ,"Shanghai ");
也是可行的。
上面一些记录的例子并不好,成员的数据类型太简单(像生日的数据类型,一般是年月日做成员的一个记录),字符串类型太多,手工赋值的话还要数一下有几个字符,实际中也很少这样的用法,一般还是用函数来赋值。这点请注意一下。
4.6 判别式的其它用途 判别式的另一个用途是动态决定其成员长度,如:
type
Buffer (Size:Integer)
is
record
High_Buffer(1..Size);
Low_Buffer(1..Size);
end record;
第5章 控制结构(Statement)
5.1 概述(Overview)
在 Ada 子程序的 “
is”和“
end”之间,是一组有序语句,每句用双引号 ;结束。这些语句大致可分成三种控制结构:顺序结构,选择结构,循环结构----如果按照前辈们辛辛苦苦的证明:任何程序都可以只由这三种结构完成。以前我们见过的简单程序都是顺序结构,本章里会介绍一下 Ada 里选择结构的if、case 语句和循环结构的 loop 语句及其变种,并介绍顺序结构中以前没讲过的 null 和块语句(block statement),最后是比较有争议的 goto 语句---好像每本教科书上都骂它,说它打破了程序的良好结构。控制结构是一门老话题,Ada95 对它也没作多大改动,语法上和其它语言还是很接近的,但可读性好一点,所有控制结构后都以"
end something"结束。
5.2 if 语句(if statement)
if 语句判断一个条件是否成立,如果成立,则执行特定的语句,否则跳过这些语句。一般格式如下:
if
condition
then
statements
end if;
当
condition 的值为 True 时,则执行
statements,否则跳过
statements,执行“end if”后面的语句。
如果当
condition 为 False 也要执行特定语句,则用下面的格式:
if
condition
then
statements
else
other statements
end if;
这样当条件不成立时,执行
other statement,而不是跳过 if 结构。
下面一种格式是为了多个条件判断而用,防止 if 语句过多:
if
condition
then
statements
elsif
condition
then
other statements
elsif
condition
then
more other statements
else
even more other statements
end if;
使用 elsif 的次数没有限制,注意 elsif 的拼写----不是elseif。在这里需要注意一下
condition 的值,一定要为布尔型,不像 C 里面,随便填个整数也没事。
下面以简单的一个例子来解释一下 if 语句:
000 -- filename: ifInteger.adb
001
with Ada.Text_IO;
use Ada.Text_IO;
002
with Ada.Integer_Text_IO;
use Ada.Integer_Text_IO;
003
procedure testrange
is
004 Var : Integer;
005
begin
006 Put ("Enter an Integer number to confirm its range:");
007 Get (Var);
008
if Var
in Integer'First .. -1
then
009 Put_Line ("It is a negative number");
010
elsif Var
in 1 .. Integer'Last
then
011
Put_Line ("It is a positive number");
012
else
013 Put_Line ("It is 0");
014
end if;
015
end testrange;
[007] 输入值 Var;[008]-[014]的语句都是测试 Var 的范围,如是负数则输出"It is a negative number",正数输出"It is a positive number",为0则输出"It is 0",以上3种情况如果都没产生,则是因为输入值非 Integer 类型或输入值过大,从而产生异常。
5.3 case 语句(case Statement)
如果所要判断的变量有多种可能,并且每种情况都要执行不同的操作,if 语句很显然繁了一点,这时就使用 case 语句,格式为:
case
expression
is
when choice1 => statements
when choice2 => statements
. . .
when
others => statements
end case;
判断 expression 的值,如符合某项choice,则执行后面的statement,如果全都不符合时,就执行
others 后的语句。choice 的值不能相同。
when
others 也可以没有,但不推荐这样做,以免有没估计到的情况产生。因此上例也可改成:
000 -- filename: ifInteger.adb
001
with Ada.Text_IO;
use Ada.Text_IO;
002
with Ada.Integer_Text_IO;
use Ada.Integer_Text_IO;
003
procedure testrange
is
004 Var : Integer;
005
begin
006 Put ("Enter an Integer number to confirm its range:");
007 Get(Var);
008
case Var
is
009
when Integer'First .. -1 =>
010 Put_Line ("It is a negative number");
011
when 1 .. Integer'Last =>
012 Put_Line ("It is a positive number");
013
when others =>
014 Put_Line ("It is 0");
015
end case;
016
end testrange;
与前面的例子完全等效。
5.4 loop 语句(loop Statement)
很多情况下,我们要反复执行同一操作,无疑这时要使用循环结构。循环结构除了最简单的loop语句,还有其变种for 和while语句。
最简单的loop语句格式为:
loop
statements
end loop;
当要退出该循环时,使用 exit 或 exit when 语句。exit表示直接退出该循环,exit when则在符合 when 后面的条件时再退出。再将testrange 改动一下,来了解loop和exit语句。
000 -- filename: ifInteger.adb
001
with Ada.Text_IO;
use Ada.Text_IO;
002
with Ada.Integer_Text_IO;
use Ada.Integer_Text_IO;
003
procedure testrange
is
004 Var : Integer;
005
begin
006
loop
007 Put ("Enter an Integer number to confirm its range:");
008 Get(Var);
009
case Var
is
010
when Integer'First .. -1 =>
011 Put_Line ("It is a negative number");
012
when 1 .. Integer'Last =>
013 Put_Line ("It is a positive number");
014
when others =>
015 Put_Line ("It is 0");
016
end case;
017
exit when Var = 0;
018
end loop;
019
end testrange;
上例循环输出"Enter an Integer number to confirm its range:",要求输入一个整数;当输入值为0时,输出"it is 0",再退出。
5.5 for 循环(for loop)
for 循环只是loop的变种,格式如下:
for
index
in [reverse]
range
loop
statements;
end loop;
*reverse 是可选的.
注意一下,
index 是for循环中的局部变量,无需额外声明,只需填入一个合法的标识符即可,在for循环内,不能修改
index的值。
index的值一般情况下是递增加1,如
for i
in 1..100,i的初值为1,每循环一次加1,直至加到100,循环100次结束;有时也需要倒过来,如i初值为100,减到1,则为
for i
in reverse 1..100。但
range中较大值在前则该循环不进行,如
for i
in [reverse] 100..1,循环内语句会略过---即变成了空语句。