Круговая семантика для доступа к срезам массива, индексированным модульным типом

Я хотел бы создать массив и получить к нему доступ следующим образом для операций чтения и записи срезов (т.е. более одного элемента одновременно):

  • Если индексы находятся в пределах диапазона, доступ к ним осуществляется как обычно.
  • Если второй индекс меньше первого индекса, доступ к данным осуществляется следующим образом: First .. A'Last & A'First .. (First + 5) (оказывается, это не работает как есть из-за того, что верхняя граница результата конкатенации выходит за пределы допустимого диапазона)

Я придумал следующий пример, чтобы продемонстрировать проблему:

with Ada.Text_IO;
use  Ada.Text_IO;

procedure Test_Modular is
    type Idx is mod 10;
    type My_Array is array (Idx range <>) of Integer;

    A: My_Array(Idx) := (
        0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5,
        6 => 6, 7 => 7, 8 => 8, 9 => 9
    );

    First: constant Idx := 7;

    S: constant My_Array := A(First .. First + 5);
begin

    for I in S'range loop
        Put_Line(Idx'Image(I) & " --> " & Integer'Image(S(I)));
    end loop;

end Test_Modular;

Как и в примере, 5 является статическим, компилятор предупреждает меня следующим образом:

$ gnatmake -o test_modular test_modular.adb
x86_64-linux-gnu-gcc-10 -c test_modular.adb
test_modular.adb:18:19: warning: loop range is null, loop will not execute
x86_64-linux-gnu-gnatbind-10 -x test_modular.ali
x86_64-linux-gnu-gnatlink-10 test_modular.ali -o test_modular

При запуске программы наблюдаю следующее:

$ ./test_modular

то есть нет вывода, как предсказывает предупреждение компилятора.

Теперь я задаюсь вопросом: есть ли способ написать срез как A(First .. First + 5) и сделать его «обтекающим», чтобы доступ к данным был таким же, как в этой модифицированной примерной программе, за исключением того, что нет необходимости явно различать два случая в коде?

with Ada.Text_IO;
use  Ada.Text_IO;

procedure Test_Modular_2 is
    type Idx is mod 10;
    type My_Array is array (Idx range <>) of Integer;

    A: My_Array(Idx) := (
        0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5,
        6 => 6, 7 => 7, 8 => 8, 9 => 9
    );

    First: constant Idx := 7;

    S1: constant My_Array := A(First .. A'Last);
    S2: constant My_Array := A(A'First .. (First + 5));
begin

    for I in S1'range loop
        Put_Line(Idx'Image(I) & " --> " & Integer'Image(S1(I)));
    end loop;
    for I in S2'range loop
        Put_Line(Idx'Image(I) & " --> " & Integer'Image(S2(I)));
    end loop;

end Test_Modular_2;
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
0
0
101
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Попробуйте следующий подход:

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is
   type Idx is mod 10;
   type My_Array is array (Idx range <>) of Integer;

   function rotate (arr : My_Array; head : Idx; tail : Idx) return My_Array is
      P1_Len : Natural;
      P2_Len : Natural;
   begin
      P1_Len :=
        (if head <= tail then Natural (tail) - Natural (head) + 1
         else Natural (arr'Last) - Natural (head) + 1);
      P2_Len := (if head <= tail then 0 else Natural (tail) + 1);
      declare
         Result : My_Array (0 .. Idx (P1_Len + P2_Len - 1));
      begin
         if head <= tail then
            Result := arr (head .. tail);
         else
            Result (0 .. Idx (P1_Len - 1))       := arr (head .. arr'Last);
            Result (Idx (P1_Len) .. Result'Last) := arr (0 .. tail);
         end if;
         return Result;
      end;
   end rotate;

   procedure print (A : My_Array) is
   begin
      for V of A loop
         Put (V'Image);
      end loop;
      New_Line;
   end print;

   A : My_Array :=
     (0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8,
      9 => 9);
   head : Idx := 7;
   tail : Idx := head + 5;

begin
   Put_Line ("Head: " & head'Image & " Tail:" & tail'Image);
   Put_Line ("Initial value order:");
   print (A);
   declare
      S1 : My_Array := rotate (A, head, tail);
   begin

      Put_Line ("Rotated value order:");
      print (S1);
   end;

end Main;

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

Спасибо, что написали это. IIUC подтверждает, что необходимо различать регистры - по сути, это часть if head <= tail, которую я надеялся избежать :)

linux-fan 28.10.2022 19:11

Кроме того, вам нужно указать срезы массива, в которые нужно «вставить» разделы. Он не работает без явного указания слайса.

Jim Rogers 28.10.2022 19:23

Учитывая, что Idx является модульным типом, альтернативным подходом является

function Rotate (Source : in My_Array; Start : in Idx; Stop : in Idx) return My_Array is
   Last   : Idx;
   Next   : Idx := Start;
   Result : My_Array (Idx);
begin -- Rotate
   Copy : for I in Result'range loop
      Result (I) := Source (Next);
      Last := I;

      exit Copy when Next = Stop;

      Next := Next + 1;
   end loop Copy;

   return Result (0 .. Last);
end Rotate;

Еще одной альтернативой с явным вычислением границ массива результатов может быть:

function Get_Wrapped_Slice(A: in My_Array; 
                              Start: in Idx;
                              Stop: in Idx) return My_Array is
      Length: constant Idx := (if Stop >= Start then (Stop - Start) else (A'Last - Start + Stop + 1));
      Result: My_Array( 0 .. Length);
      Cursor: Idx := Start;
   begin
      for I of Result loop
         I := A(Cursor);
         Cursor := Cursor + 1;
      end loop;
      return Result;
   end Get_Wrapped_Slice;

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