I would like to create an array and access it in the following way for read and write slice operations (i.e. more than one element at once):
First .. A'Last & A'First .. (First + 5)
(turns out this doesn't work as-is due to concatenation result upper bound out of range)I have come up with the following example to demonstrate the issue:
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;
As in the example, the 5
is static, the compiler warns me as follows:
$ 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
When running the program, I observe the following:
$ ./test_modular
i.e. no output as predicted by the compiler warning.
Now I wonder: Is there a way to write the slice like A(First .. First + 5)
and make it “wrap around” such that the data accessed will be the same as in this modified example program except without having to distinguish the two cases in the code explicitly?
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;
Try the following approach:
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;
The rotate function above rotates the set of values indicated by the head and tail index values, placing the head value at the start of the array returned by rotate. This example shows that the resulting array may contain fewer elements than the array passed as a parameter to the function.