Как динамически создать массив фиксированного размера в Аде?

В рамках изучения Ады я решаю некоторые основные задачи по программированию. Я столкнулся с ситуацией, когда я хотел бы создать двумерный массив целых чисел фиксированного размера (размер определяется во время выполнения). Моя идея заключалась в том, чтобы иметь небольшую служебную функцию, которой можно было бы передать размер массива, создать его, заполнить и вернуть для использования в других функциях.

Как мне это сделать? Другие ответы, которые я видел, сохраняют созданный массив в области функций и не возвращают его.

Пока это моя основная процедура:

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_Type объявляется в grid.ads как:

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

В этих файлах Coords.Box — это простая запись, содержащая минимальные/максимальные целые числа X/Y.

Если я запущу это и введу разумные числа для размера сетки, я получу CONSTRAINT_ERROR.

Я прочитал этот ответ и этот ответ и некоторые другие менее связанные ответы, и я просто не понимаю.

Я новичок в Аде, но знаю другие языки.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
503
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Размер объекта массива не может быть изменен после объявления объекта. Хотя это может показаться проблемой, Ада предоставляет решение путем объявления объекта во внутреннем блоке.

В приведенном ниже примере тип вашего массива определяется как неограниченный массив 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, используя значения, считанные из ввода. Функция просто возвращает объект из внутреннего блока. Раздел 5.6 Справочного руководства по Ада описывает операторы блока Ада.

В следующем примере показано, как это работает для массива целых чисел:

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

Я думал, что переменные declare были локальными по объему/времени жизни блока? Или в данном случае по-другому? Или у меня там что-то не так?

jfowkes 08.04.2019 23:23

Кроме того, я добавил свой Grid_Type к вопросу, я случайно пропустил это.

jfowkes 08.04.2019 23:23
Make_Grid возвращает копию New_Grid.
Simon Wright 08.04.2019 23:56

Переменные являются локальными для блока, в котором они объявлены, точно так же, как переменные, объявленные в функциональном блоке, являются локальными для функционального блока. Эта локальность не означает, что объект не может быть возвращен из функции.

Jim Rogers 09.04.2019 00:03

Ах, я использовал мысленную модель этого, как будто это был C/C++, возвращающий указатель на объект, созданный с помощью malloc. Что явно неправильно!

jfowkes 10.04.2019 11:00
return New_Grid : Grid_Type(… dims …) := (others => (others => 0)); будет короче и позволит избежать копирования потенциально большой структуры данных.
flyx 16.04.2019 11:59

Значение по-прежнему копируется при возврате к объекту, определенному в вызывающей области. Копирование не предотвращается приведенным выше предложением.

Jim Rogers 16.04.2019 18:41

@JimRogers Можете ли вы подтвердить это утверждение? Согласно Ада 2005 обоснование: Другими словами, адрес X передается как скрытый параметр функции Make, чтобы создать пространство для R. Поэтому копирование никогда не выполняется.

flyx 17.04.2019 13:11

@flyx Раздел, который вы цитируете в обосновании Ады 2005, относится к возврату ограниченных типов. Ограниченные типы не могут быть переданы копированием, так как для ограниченных типов нет операции присваивания. Это требует прохождения по типу доступа. Неограниченные типы не имеют этого ограничения и передаются путем копирования.

Jim Rogers 17.04.2019 18:51

В дополнение к использованию блоков объявления для временного хранения результата вашей функции вы также можете использовать Ада.Контейнеры.Индефините_Холдерс для более постоянного хранения результата.

Вам нужно будет создать экземпляр общего

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));

как только вы это сделаете, вы можете получить доступ к сетке, используя операцию Reference:

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 существует вне блока объявления, поэтому он остается доступным.

Другие вопросы по теме