How to std::rotate in Ada?

Viewed 117

Does an equivalent to std::rotate exist in the Ada standard libraries? If not, is there a consensus in the Ada community of some non-standard algorithms library for this sort of operation? I found the Ada Standard Generic Library but it was a proof of concept from 1996 and does not include rotate for arrays (it does for trees, though).

I whittled down the demo on the cppreference page linked above:

#include <algorithm>
#include <iostream>
#include <vector>

auto print = [](auto const& remark, auto const& v) {
    std::cout << remark;
    for (int n : v)
        std::cout << n << ' ';
    std::cout << '\n';
};

int main()
{
    std::vector<int> v{2, 4, 2, 0};

    print("before rotate:\t\t", v);

    // simple rotation to the left
    std::rotate(v.begin(), v.begin() + 1, v.end());

    print("simple rotate left:\t", v);

    // simple rotation to the right
    std::rotate(v.rbegin(), v.rbegin() + 1, v.rend());

    print("simple rotate right:\t", v);
}

The equivalent Ada would be this, if I had a drop-in replacement for the std::rotate calls:

with Ada.Text_IO;
with Ada.Characters;
with Ada.Characters.Latin_1;

procedure Main is
   Tab : Character renames Ada.Characters.Latin_1.HT;

   type Number_List_Type is array (1 .. 4) of Integer;

   procedure Print (Remark : String; Numbers : Number_List_Type) is
      use Ada.Text_IO;
      Endline : Character renames Ada.Characters.Latin_1.LF;
   begin
      Put (Remark);
      for N of Numbers loop
         Put (N'Image & ' ');
      end loop;
      Put (Endline);
   end;

   V : Number_List_Type := (2, 4, 2, 0);
begin
   print("before rotate:" & Tab & Tab, v);

   -- simple rotation to the left
   -- std::rotate(v.begin(), v.begin() + 1, v.end());

   print("simple rotate left:" & Tab, v);

   -- simple rotation to the right
   -- std::rotate(v.rbegin(), v.rbegin() + 1, v.rend());

   print("simple rotate right:" & Tab, v);
end Main;

Expected output:

before rotate:          2 4 2 0
simple rotate left:     4 2 0 2
simple rotate right:    2 4 2 0
2 Answers

In Ada you can use slices, concatenation, and assignment to rotate array elements:

v := v(2..4) & V(1..1);

if you need a generic solution you can do something like:

generic
    type Element_Type is private;
    type Index_Type is (<>);
    type Element_Array is array(Index_Type range <>) of Element_Type;
function Rotate_1(Source: Element_Array; N_First : Index_Type) return Element_Array;

function Rotate_1(Source: Element_Array; N_First : Index_Type) return Element_Array is
    Source_Last  : constant Index_Type := Source'Last;
    Source_First : constant Index_Type := Source'First;
begin

    if N_First < Source_First then
        raise Constraint_Error;
    end if;
    
    if N_First = Source_First then
        return Source;
    end if;

    return Result : Element_Array(Source'Range) 
        :=   Source(N_First     .. Source_Last) 
           & Source(Source_First.. Index_Type'Pred(N_First));
end Rotate_1;

generic
    type Element_Type is private;
    type Index_Type is (<>);
    type Element_Array is array(Index_Type range <>) of Element_Type;
procedure Rotate(Source: in out Element_Array; N_First : Index_Type);

procedure Rotate(Source: in out Element_Array; N_First : Index_Type) is
    function r is new Rotate_1(Element_Type,Index_Type,Element_Array);
begin
    Source := R(Source,N_First);
end Rotate;

Note that my generic example expects an unconstrained array type, so you would change your array type declaration to:

type Number_List_Type is array (Positive range <>) of Integer;

If you want to rotate the actual parameter rather than providing a rotated copy you can do the following;

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is
   type number_list is array (Positive range <>) of Integer;
   
   procedure rotate_left (Item : in out number_list) is
   begin
      Item := Item(Item'First + 1 .. Item'Last) & Item(Item'First);
   end rotate_left;
   
   procedure rotate_right (Item: in out number_list) is
   begin
      Item := Item(Item'Last) & Item(Item'First .. Item'Last - 1);
   end rotate_right;
   
   procedure print(Remark : in String; Item : in number_list) is
   begin
      Put_Line(Remark);
      for C of Item loop
         Put(C'Image & " ");
      end loop;
      New_line;
   end print;
   
   L1 : number_list := (2, 4, 2, 0);
   L2 : number_list := (4, 5, 6, 1, 3);
                               
begin
   print ("L1 before rotate: ", L1);
   rotate_left(L1);
   print ("L1 rotated left:  ", L1);
   rotate_right(L1);
   print ("L1 rotated right: ", L1);
   print ("L2 before rotate: ", L2);
   rotate_left(L2);
   print ("L2 rotated left:  ", L2);
   rotate_right(L2);
   print ("L2 rotated right: ", L2);
end Main;

The result is:

L1 before rotate: 
 2  4  2  0 
L1 rotated left:  
 4  2  0  2 
L1 rotated right: 
 2  4  2  0 
L2 before rotate: 
 4  5  6  1  3 
L2 rotated left:  
 5  6  1  3  4 
L2 rotated right: 
 4  5  6  1  3 
Related