如何在Ada中动态创建固定大小的数组?

时间:2019-04-08 20:01:45

标签: ada

作为学习Ada的一部分,我正在处理一些基本的编码挑战。我遇到了一种情况,我想创建一个固定大小的2D整数数组(大小在运行时确定)。我的想法是要有一个小的实用程序函数,该函数可以传递数组的大小,创建它,填充它并返回它以供其他函数使用。

我该怎么做?我看到的其他答案将创建的数组保留在function-scope中,并且不返回它。

到目前为止,这是我的主要步骤:

with Ada.Integer_Text_IO;
with Ada.Text_IO;

with Coord;
with Grid;

procedure test is

   boundary : Coord.Box;

   -- This is the array I want to create and fill
   -- Note sure about what I put here for the "initial" size
   new_grid : Grid.GridType (0 .. 1,  0 .. 1);

begin

   --  This is just for the example, actually these
   --  values are calculated from values in a text file 
   Ada.Text_IO.Put ("X Min?");
   Ada.Integer_Text_IO.Get (boundary.min.x);
   Ada.Text_IO.Put ("X Max?");
   Ada.Integer_Text_IO.Get (boundary.max.x);
   Ada.Text_IO.Put ("Y Min?");
   Ada.Integer_Text_IO.Get (boundary.min.y);
   Ada.Text_IO.Put ("Y Max?");
   Ada.Integer_Text_IO.Get (boundary.max.y);

   new_grid := Grid.get_grid (boundary);

   Grid.print (new_grid);

end test;

这是grid.adb,其中是get_grid函数:

with Ada.Integer_Text_IO;
with Ada.Text_IO;

package body Grid is

   function get_grid (bounds : Coord.Box) return GridType is
      --  This is the grid I'd like to return
      new_grid : Grid.GridType (bounds.min.x .. bounds.max.x, bounds.min.y .. bounds.max.y);
   begin
      for X in bounds.min.x .. bounds.max.x loop
         for Y in bounds.min.y .. bounds.max.y loop
            new_grid (X, Y) := X + Y;
         end loop;
      end loop;
      return new_grid; -- Needs to persist outsde this function
   end get_grid;

   --  Print function removed for clarity (this works)

end Grid;

Grid_Typegrid.ads中声明为:

type GridType is array (Integer range <>, Integer range <>) of Integer;

在这些文件中,Coords.Box只是保存X / Y最小/最大整数的简单记录。

如果我运行此命令并为网格大小输入合理的数字,则会得到CONSTRAINT_ERROR

我已经读过this answerthis answer以及其他一些不太相关的答案,但我还是没听懂。

我是Ada的新手,但是精通其他语言。

2 个答案:

答案 0 :(得分:6)

声明数组对象后,无法更改其大小。尽管这似乎是一个问题,但Ada提供了在内部块中声明对象的解决方案。

下面的示例将您的数组类型定义为element_type的不受约束的数组。替换您希望数组包含在实际代码中的任何类型。这种不受限制的类型使您可以创建具有任何所需尺寸的类型的实例。

type Grid_Type is array(Natural range <>, Natural range <>) of element_type;

在函数中读取数组边界信息,然后使用这些数组边界声明一个实例。

function Make_Grid return Grid_Type is
   Dim1_Min, Dim1_Max : Natural;
   Dim2_Min, Dim2_Max : Natural;
begin
   get(Dim1_Min);
   get(Dim1_Max);
   get(Dim2_Min);
   get(Dim2_Max);

   declare
      New_Grid : Grid_Type(Dim1_Min..Dim1_Max, Dim2_Min..Dim2_Max);
   begin
      return New_Grid;
   end;
end Make_Grid;

内部块使用从输入读取的值创建Grid_Type的新实例。该函数只是从内部块返回对象。 《 Ada参考手册》的5.6节介绍了Ada块语句。

下面的示例演示了它如何用于整数数组:

package Grids is
   type Grid_type is array(Natural range <>, Natural range <>) of Integer;
   function make_grid return Grid_Type;
end Grids;


with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

package body Grids is

   ---------------
   -- make_grid --
   ---------------

   function make_grid return Grid_Type is
      Dim1_Min, Dim1_Max : Natural;
      Dim2_Min, Dim2_Max : Natural;
   begin
      Get(Dim1_Min);
      Get(Dim1_Max);
      Get(Dim2_Min);
      Get(Dim2_Max);

      declare
         New_Grid : Grid_Type(Dim1_Min..Dim1_Max, Dim2_Min..Dim2_Max) :=
           (Others =>(Others => 0));
      begin
         return New_Grid;
      end;

   end make_grid;

end Grids;

该程序的主要测试内容是:

with Ada.Text_IO; use Ada.Text_IO;
with Grids; use Grids;

procedure Grids_Test is
   The_Grid : Grid_type := Make_Grid;
begin
   Put_Line("Grid Dimensions");
   Put_Line(Natural'Image(The_Grid'First(1)) & ".." &
              Natural'Image(The_Grid'Last(1)) & " , " &
              Natural'Image(The_Grid'First(2)) & "..." &
                Natural'Image(The_Grid'Last(2)));
end Grids_Test;

示例执行的输入和输出为:

0
10
20
30
Grid Dimensions
 0.. 10 ,  20... 30

答案 1 :(得分:1)

除了使用声明块临时保存函数的结果外,您还可以使用Ada.Containers.Indefinite_Holders来更永久地存储结果。

您需要实例化通用

use type Grid.Grid_Type;  -- necessary to get the "=" operation
package Grid_Holders is new Ada.Containers.Indefinite_Holders(Grid.Grid_Type);

您将其声明为

New_Grid : Grid_Holders.Holder;

,并且在运行时可以使用

对其进行初始化
New_Grid := Grid_Holders.To_Holder(Grid.Get_Grid(Boundary));

完成此操作后,可以使用“参考”操作访问网格:

Grid.Print(New_Grid.Reference);

如果要直接访问元素,则可能必须执行以下操作:

Grid.Reference.Element(1,2) := 23;

请注意,某些版本的GNAT存在一个错误,该错误会生成Finalization异常。如果是这种情况,通常可以通过使用声明块或函数来解决它。声明块示例:

declare
   The_Grid : Grid.Grid_Type renames New_Grid.Reference;
begin
   The_Grid(1,2) := 23;
end;

这与其他答案的声明块示例不同,因为此声明块不会创建任何新的网格,而只是访问现有内存。纯粹是为了解决编译器错误。在此示例中,New_Grid存在于声明块之外,因此它保持可用。