ADA 95教程 数组
我们的第一个阵列
Example program ------> e_c10_p1.ada
-- Chapter 10 - Program 1 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Array1 is N : INTEGER := 10; Dummy1 : array(INTEGER range 1..7) of BOOLEAN; Dummy2 : array(INTEGER range -21..N) of BOOLEAN; Dummy3 : array(-21..N) of BOOLEAN; type MY_ARRAY is array(1..5) of INTEGER; Total : MY_ARRAY; First : MY_ARRAY; Second : MY_ARRAY; Funny : array(1..5) of INTEGER; X,Y : array(12..27) of INTEGER; Fourth_Value : INTEGER renames First(4); begin First(1) := 12; First(2) := 16; First(3) := First(2) - First(1); Fourth_Value := -13; First(5) := 16 - 2 * First(2); for Index in 1..5 loop Second(Index) := 3 * Index + 77; end loop; Total := First; if Total = First then Put("Both arrays are the same size and contain "); Put_Line("the same values in all elements."); end if; for Index in 1..5 loop Total(Index) := Total(Index) + Second(Index); Funny(Index) := Total(Index) + First(6 - Index); Put("The array values are"); Put(Total(Index), 6); Put(Funny(Index), 6); New_Line; end loop; end Array1; -- Result of execution -- Both arrays are the same size and contain the same values in... -- The array values are 92 76 -- The array values are 99 86 -- The array values are 90 94 -- The array values are 76 92 -- The array values are 76 88
数组是由两个或多个类型相同的元素组成的组。在Ada中,与大多数现代计算机语言一样,数组可以由许多不同类型的数据组成,但数组的所有元素必须是同一类型的。最好的方法是检查名为e_c10_p1.ada的程序,该程序包含一些数组示例。
如何声明下标?
为了简单起见,我们将从第8行开始,在那里我们有一个名为Dummy1的数组声明。这一行表示名为Dummy1的变量将有7个从1到7的元素,每个元素将能够存储一个布尔变量。我们很快就会看到,单个元素将由变量名和括号中的下标来引用,或者Dummy1(1),Dummy1(2),。。。至Dummy1(7)。请记住,每个都是一个布尔变量。
为了定义一个数组,我们使用保留字array和带有适当修饰符的of,如本例所示。我们定义数组将覆盖的范围、范围变量的类型(必须由离散类型限制组成)以及数组中每个元素的类型。请记住,离散类型是整数类的任何类型,包括枚举类型。在本教程的第2部分中,我们将有一个带有枚举数组索引的示例程序。
让我们看看另一个数组声明
在第9行中,我们将Dummy2定义为一个布尔型变量数组,它覆盖Dummy2(-21)到Dummy2(10)的范围,因为N的值是10。第10行说明了如何将数组Dummy3声明为从Dummy3(-21)到Dummy3(10)的布尔变量数组,但是这次没有显式地声明下标类型,只是由下标限制的值暗示。实际上,前两种方法也不需要下标类型,因为它们是隐含的。整型经常用于数组下标,如果没有给定整型,则将整型定义为默认值。这样做只是为了让数组的使用更简单一点。
到目前为止,在这个程序中,我们已经定义了大约70个没有初始值的变量,因为我们没有给它们赋值。稍后我们将看到如何初始化数组,但首先我们将学习如何使用它们。
如何定义数组类型?
第12行给出了声明数组类型的一般方法。它使用保留字type,后跟类型名称,保留字is,然后是类型的定义。定义由范围组成,后跟的保留字和元素类型。我们现在有了一个类型名,可以用来定义任意数量的数组变量,每个变量都由整数变量组成。每个元素也有5个元素,覆盖范围从1到5。因此,第14行定义了一个名为Total的五个元素的数组,其中的元素将被命名为Total(1)、Total(2)。。。Total(5)。这些元素中的每一个都可以用来存储一个INTEGER类型的值。
程序的第22行说明了如何将值12赋给First的一个元素,First是另一个My_Array类型的数组,因此由5个元素组成。第二个元素被赋值为16,第三个元素被赋值为从第二个元素中减去第一个元素得到的值,得到值4。这说明了元素赋值后的用法。第四和第五个元素也被指定为无意义的数据,说明了一些数学运算。第25行的作业将在下一段解释。如果您记得每个元素都是整数类型变量,那么您可以像使用任何其他整数类型变量一样使用它们,只是您必须添加下标以指示您在程序的每个点上感兴趣使用哪个元素。
重命名数组元素
第19行是重命名数组中单个元素的图示。同样,这只是给了我们一个同义词,我们可以用它来指代变量,它不声明另一个变量。应该指出的是,不允许重命名类型,但是可以通过声明与父类型具有相同范围的子类型来实现相同的效果。
什么是RANGE_ERROR?
任何试图使用超出指定范围的下标的操作都将导致系统引发异RANGE_ERROR,并将作为致命错误处理,除非您自己处理异常,否则程序将终止操作。我们将在本教程的第2部分研究异常。
下标可以是一个变量本身,只要它是正确的类型,如第29行所示,其中名为Second的数组的所有五个元素都被指定为无意义数据以供说明。下标也可以以任意复杂度进行计算,当然前提是它产生整数类型的结果并且在声明的下标的范围内。
数组赋值
第32行中的赋值是合法的,前提是这两个数组的类型相同,因此名为First的数组的所有5个值都被赋值给名为Total的数组的5个元素。第33行说明,甚至可以比较数组是否相等或不相等,如果Total的每个元素等于First的相应元素,则认为它们相等。如果有任何不平等,结果是假的。
第38行到第45行说明了数组上允许的更多操作。你自己研究它们应该没问题。
什么是匿名类型?
在第12行中,我们定义了一个类型并给它起了一个名字,然后我们可以在程序的其余部分随意使用这个名字。任何这种类型的数组都被称为My_Array类型,因为它有一个指定给它的名称。第17行中声明的数组没有与其关联的名称,因此它被称为匿名类型。
第32行中的数组赋值是唯一可能的,因为这两个数组具有相同的确切类型,并且为了具有相同的类型,必须使用相同的类型名声明它们。名为Funny的数组与First数组具有相同的结构,但它不是用相同的类型名声明的,因此它属于不同的类型,不能在数组赋值语句中使用。由于数组是匿名类型,因此不可能用相同的类型定义另一个数组,因此不可能在赋值语句中将此数组与任何其他数组一起使用。
第18行在同一条语句中声明了数组X和Y,看起来它们应该具有赋值兼容性,但是根据Ada的定义,这两个数组的类型不同,因为在一条语句中命名两个变量名只是在两行中命名它们的一种简写方法。因此,这两个数组都是单独的匿名类型。这是一个很好的观点,但应该清楚地理解。
只有使用相同的类型名声明两个数组时,它们才是赋值兼容的。编译并运行此程序,并将输出与预期的输出进行比较。
什么是数组的切片?
Example program ------> e_c10_p2.ada
-- Chapter 10 - Program 2 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Slice is type MY_ARRAY is array(1..12) of INTEGER; First, Second : MY_ARRAY; Funny : array(1..33) of INTEGER; begin for Index in 1..33 loop Funny(Index) := 100 + Index * 2; end loop; for Index in 1..12 loop First(Index) := Index; end loop; Second(1..5) := First(3..7); Second(1..4) := First(6..9); Second(7..12) := First(3..8); First(2..9) := First(5..12); First(1..12) := MY_ARRAY(Funny(1..12)); First(1..12) := MY_ARRAY(Funny(16..27)); for Index in 1..12 loop Put("The array named First has the values "); Put(First(Index)); New_Line; end loop; end Slice; -- Result of execution -- The array named First has the values 132 -- The array named First has the values 134 -- The array named First has the values 136 -- The array named First has the values 138 -- The array named First has the values 140 -- The array named First has the values 142 -- The array named First has the values 144 -- The array named First has the values 146 -- The array named First has the values 148 -- The array named First has the values 150 -- The array named First has the values 152 -- The array named First has the values 154
名为e_c10_p2.ada的程序包含几个在ada中使用切片的示例,它是数组的一部分。您可能希望在一条语句中将一个数组的一部分赋给另一个数组的一部分。这可以通过切片来完成。
我们首先声明一个数组类型My_Array,然后用它声明两个数组,第一个和第二个。最后,我们声明了第三个名为Funny的数组,它是匿名类型,我们在研究上一个示例程序时解释了这一点。当前的示例程序将说明使用匿名类型数组的困难,但我们将从使用命名数组开始。
什么是切片?
在程序的可执行部分,我们将无意义的值赋给名为Funny和First的数组,这样我们就可以处理一些数据。然后在第22行中,我们告诉系统获取名为First的数组的元素3到7,并将它们赋给名为Second的数组的元素1到5。赋值运算符两边的项是一个切片,数组的一部分。为了完成如图所示的片分配,两个数组必须具有相同的类型,并且两个片必须具有相同的元素数。当然,所有切片限制必须在所用类型的下标声明的范围限制内。第23行说明从第一个元素复制到第二个元素,第24行说明复制6个元素。
在第25行中,八个元素从一个数组复制到它自己,这样数组的目的地和原点部分就重叠了。Ada的定义使得所有的值都以真实的方式复制,而不是在继续复制值时重新复制一些先前复制的值。这是因为在对左侧变量赋值之前,会对整个右侧表达式求值。请注意,切片只能用于一维数组。
返回匿名类型变量
我们说过名为Funny的变量是匿名类型,它会导致一些困难,所以让我们看看问题是什么。为了将一个数组的全部或部分赋给另一种类型的数组,我们必须使用类型转换或得到编译错误。第27行演示了如何使用类型转换,正如我们前面所看到的。但是,我们只能基于整个数组(而不是数组的一部分)进行类型转换,因此只能将该类型从匿名数组转换为目标类型My_Array的全尺寸数组。因此,我们只能将匿名类型变量中的一个片段复制到目标的完整数组中。无法将类型从My_Array转换为匿名类型,因为匿名类型没有名称,所以不能使用片来分配给名为Funny的数组变量。第28行说明了对名为First的完整数组的另一个片分配。
将显示名为First的整个数组以供观察。编译并运行这个程序并检查输出。
多维数组
Example program ------> e_c10_p3.ada
-- Chapter 10 - Program 3 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure MultAry1 is type MATRIX is array(INTEGER range 1..3, INTEGER range 1..4) of INTEGER; Square_Board : MATRIX; Checker_Board : MATRIX; Chess_Board : array(INTEGER range 1..3, INTEGER range 1..4) of INTEGER; Across, Over : INTEGER; begin for Across in 1..3 loop for Over in 1..4 loop Square_Board(Across, Over) := Across * Over; Chess_Board(Across, Over) := 0; end loop; end loop; Checker_Board := Square_Board; Checker_Board(2, 3) := 2; Checker_Board(Checker_Board(2, 3), 4) := 17; Checker_Board(3, 3) := Chess_Board(3, 3); for Across in 1..3 loop for Over in 1..4 loop Put(Checker_Board(Across, Over), 5); end loop; New_Line; end loop; end MultAry1; -- Result of execution -- 1 2 3 4 -- 2 4 2 17 -- 3 6 0 12
检查名为e_c10_p3.ada的程序,了解多维数组的第一个示例。我们首先声明一个名为MATRIX的类型,它由一个数组的数组组成,该数组共有12个元素。数组的每个元素都由两个下标引用,下标紧跟在括号中的变量名后面,如程序的可执行部分所示。第18行到第23行包含一个嵌套循环,用乘法表填充名为Square_Board的变量,用全零填充Chess_Board。请注意,名为Chess_Board的变量是匿名类型,因为没有与之关联的类型名。
名为Square_Board的整个数组被分配给第25行中的array Checker_Board,这是合法的,因为它们的类型相同,这意味着它们是用相同的类型名定义的。第27行用于将值2赋给Checker_Board的一个元素,该值用于第28行,该行表示“Checker_Board(2,4):=17;”,因为第27行将值2赋给Checker_Board(2,3)。
根据上一个程序的讨论,您应该清楚,尽管Chess_Board与 Square_Board的结构相同,但它们不兼容类型,也不兼容赋值。但是,各个元素是可分配的。
将显示名为Checker_Board的数组,您可以在编译和运行此程序时观察该数组。顺便说一下,在本例中,数组名的选择很差,因为棋盘不是3乘4。良好的命名约定有助于开发高质量的软件。
我们需要一些灵活性
Example program ------> e_c10_p4.ada
-- Chapter 10 - Program 4 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure MultAry2 is SIZE : constant := 3; NEXT : constant := SIZE + 1; type MATRIX is array(INTEGER range 1..SIZE, INTEGER range 1..NEXT) of INTEGER; Square_Board : MATRIX; Checker_Board : MATRIX; Chess_Board : array(INTEGER range 1..SIZE, INTEGER range 1..NEXT) of INTEGER; Across, Over : INTEGER; begin for Across in 1..SIZE loop for Over in 1..NEXT loop Square_Board(Across, Over) := Across * Over; Chess_Board(Across, Over) := 0; end loop; end loop; Checker_Board := Square_Board; Checker_Board(2, 3) := 2; Checker_Board(Checker_Board(2, 3), 4) := 17; Checker_Board(3, 3) := Chess_Board(3, 3); for Across in 1..SIZE loop for Over in 1..NEXT loop Put(Checker_Board(Across, Over), 5); end loop; New_Line; end loop; end MultAry2; -- Result of execution -- 1 2 3 4 -- 2 4 2 17 -- 3 6 0 12
以前的程序对所有范围和循环定义都使用固定值,这使得灵活性非常小,因此被认为是糟糕的编程实践。当然,这是为了清晰起见,因为这是您第一次看到多维数组。下一个程序名为e_c10_p4.ada,它更加灵活,并演示了一些可以与ada一起使用的稍微高级的技术。
这个程序与上一个程序相同,只是在声明部分定义了两个常量,然后用它们来定义数组和循环的限制。如果您需要使程序覆盖更大的范围,那么修改常量并重新编译程序就很简单了。编译并运行这个程序,您将看到它做的事情与上一个完全相同。
我们需要更多的灵活性
Example program ------> e_c10_p5.ada
-- Chapter 10 - Program 5
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure MultAry3 is
SIZE : constant := 3;
NEXT : constant := SIZE + 1;
type MATRIX is array(INTEGER range 1..SIZE,
INTEGER range 1..NEXT) of INTEGER;
Square_Board : MATRIX;
Checker_Board : MATRIX;
Chess_Board : array(INTEGER range 1..SIZE,
INTEGER range 1..NEXT) of INTEGER;
Across, Over : INTEGER;
begin
for Across in 1..Square_Board'LAST(1) loop
for Over in 1..Square_Board'LAST(2) loop
Square_Board(Across, Over) := Across * Over;
Chess_Board(Across, Over) := 0;
end loop;
end loop;
Checker_Board := Square_Board;
Checker_Board(2, 3) := 2;
Checker_Board(Checker_Board(2, 3), 4) := 17;
Checker_Board(3, 3) := Chess_Board(3, 3);
for Across in Checker_Board'RANGE(1) loop
for Over in Checker_Board'RANGE(2) loop
Put(Checker_Board(Across, Over), 6);
end loop;
New_Line;
end loop;
end MultAry3;
-- Result of execution
-- 1 2 3 4
-- 2 4 2 17
-- 3 6 0 12
检查名为e_c10_p5.ada的程序,您会发现它与前两个程序相同,只是我们定义循环限制的方式不同。回想一下我们在上一课中介绍的有关属性的信息,本程序的新增内容将很容易让您理解。在第21行中,我们使用属性LAST来定义外循环的上限,并在括号中添加数字“1”,告诉系统我们对名为Square_Board的数组的第一个下标感兴趣。第22行使用“2”表示Square_Board第二下标的最后一个值。如果没有给出下标指示,系统将默认为“1”,但是读者在括号中指示“1”要清楚得多。在你看来,用这种方法来定义极限是一件很麻烦的事,但是当我们开始编写通用过程时,我们需要这里给出的灵活性。通用程序也有很长的路要走,但当我们研究它们时,这些技术将是绝对必要的。
第34行和第35行也使用两个循环范围的属性,但是它们再次使用括号中所需下标数的RANGE属性。
当使用单下标数组时,在括号中也使用“1”是合法的,但是如果没有给出,系统将默认为1。您应该在多维数组中显式地包含该数字,而对于单维数组,则应省略该数字,以便于编程清晰。
如何初始化数组?
Example program ------> e_c10_p6.ada
-- Chapter 10 - Program 6 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure ArryInit is type MY_ARRAY is array(1..5) of INTEGER; Total : MY_ARRAY := (12, 27, -13, 122, 44); First : MY_ARRAY := (1 => 14, 2 => 57, 3=> 111, 5 => -27, 4 => 21); Another : MY_ARRAY := (2 => 13, 5 => 57, others => 3); One_More : MY_ARRAY := (2..4 => 13, 1 | 5 => 27); type MATRIX is array(INTEGER range 1..3, INTEGER range 1..4) of INTEGER; Square_Board : MATRIX := ((4, 7, 3, 5), (3, 8, 2, 0), (1, 5, 9, 9)); Checker_Board : MATRIX := (2 => (3, 8, 2, 0), 3 => (1, 5, 9, 9), 1 => (4, 7, 3, 5)); Chess_Board : MATRIX := (2 => (3, 8, 2, 0), 3 => (1, 5, 9, 9), 1 => (4 => 5, 2 => 7, 1 => 4, 3 => 3)); begin if Square_Board = Checker_Board then Put_Line("The two arrays are equal."); end if; if Chess_Board = Square_Board then Put_Line(" and so are the other two."); end if; end ArryInit; -- Result of execution -- The two arrays are equal. -- and so are the other two.
检查名为e_c10_p6.ada的程序以获取一些数组初始化的示例。声明了七个数组,并说明了聚合表示法(位置表示法和命名表示法)。聚合是一组数字文字,虽然也可以包含枚举值,但在Ada的许多地方都使用这些数值。在这个示例程序中,我们将使用聚合来初始化数组。文字可以按使用顺序分组,这称为位置聚合。文字也可以在命名聚合中,其中每个值的用法是通过使用应该分配给它的位置的名称来定义的。
变量Total使用位置符号初始化,变量Frist使用命名符号初始化。数组初始化不允许使用混合表示法。第13行中名为Another的数组包含一个新的构造,它将保留字others与命名聚合表示法结合使用。如果包含,则必须是聚合中的最后一个条目。Another(1)、Another(3)和Another(4)的值将被初始化为值3。第15行中名为One_More的数组说明了将一系列变量初始化为值13,将两个变量初始化为值27。请注意,此处可以包含其他案例,但必须是最后一个案例。其他单维数组应该不会给您带来任何问题,但是关于多维数组的一些注释是有序的。
尽管不允许对数组使用混合聚合表示法,但该规则仅应用于一个级别,因此可以对每个级别使用不同的方法。
变量Square_Board使用所有位置表示法,但是Checker_Board对第一个下标使用命名聚合,对第二个下标使用位置表示法。Chess_Board对第一个下标使用一个命名聚合,对第二个下标使用两种方法,使事情更加复杂,尽管它在每个子组中是一致的,因此遵守规则。
稍后将提供更多数组示例
关于数组还有很多要说的,但是我们必须等到我们再讨论几个主题。这是为了让您开始使用数组,但在后面的一章中,我们将介绍其他数组主题。
编程练习
1.修改e_c10_p1.ada,使变量X和Y与赋值兼容。(Solution)
2.编写一个包含两个数组(每个数组大小为3×5)的程序,并将每个数组初始化为一组合适的整数值。将这些值逐元素相乘,并将值存储在第三个数组中。最后,在监视器上以清晰的格式显示结果(Solution)
---------------------------------------------------------------------------------------------------------------------------
原英文版出处:https://perso.telecom-paristech.fr/pautet/Ada95/a95list.htm
翻译(百度):博客园 一个默默的 *** 的人