Я хотел бы создать массив и получить к нему доступ следующим образом для операций чтения и записи срезов (т.е. более одного элемента одновременно):
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;
Попробуйте следующий подход:
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 вращает набор значений, указанных значениями индекса начала и конца, помещая значение заголовка в начало массива, возвращаемого функцией поворота. Этот пример показывает, что результирующий массив может содержать меньше элементов, чем массив, переданный функции в качестве параметра.
Кроме того, вам нужно указать срезы массива, в которые нужно «вставить» разделы. Он не работает без явного указания слайса.
Учитывая, что 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;
Спасибо, что написали это. IIUC подтверждает, что необходимо различать регистры - по сути, это часть
if head <= tail
, которую я надеялся избежать :)