Как бы вы написали универсальную функцию Ada, работающую с дублирующими списками любого типа элемента? - PullRequest
2 голосов
/ 02 октября 2019

Если возможно, как бы я написал универсальную функцию Ada, работающую с двухэлементными списками любого типа элемента? Следующая спецификация функции иллюстрирует то, что я хочу, ограничивая Array_Type массивом данного element_type.

generic
   type element_type is private;
   type Array_Type is array (Positive range <>) of element_type;
procedure Shuffle_Array (List : in out Array_Type);

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

procedure IntArrayFoo is new Foo(element_type => Integer, array_type => Integer_Array);

Так что в случае массивов, вы можете аккуратно генерировать точно то, что может отличаться в экземплярах. Тем не менее, попытка сделать аналог с Ada.Containers.Doubly_Linked_List (T) .List приводит к проблемам.

Попытка 1 - универсальная процедура создания экземпляра пакета duply_linked_list

with Ada.Containers.Doubly_Linked_Lists;

package shufit is
   generic
      type element_type is private;
      package int_dll is new Ada.Containers.Doubly_Linked_Lists(Element_Type => Integer);
      type int_dll_type is new int_dll.List;
   procedure Foo (List : in out int_dll_type);
end shufit;

Этот подход выиграл 'не работает, потому что мы не можем создать пакет в общем разделе. Даже если бы мы могли это сделать, при попытке вызвать это нам нужно сделать int_dll_type вне определения функции и все же сопоставить его - что, похоже, не работает в Ada. В C ++ я могу создать «вектор» в двух совершенно разных пространствах имен / классах, и все же они ссылаются на один и тот же тип. Но в Ada я не могу ссылаться на эквивалент в двух разных пакетах, без одного, включая другой.

Попытка 2 - универсальная процедура создания экземпляра пакета duply_linked_list

with Ada.Containers.Doubly_Linked_Lists;

generic
   type element_type is private;
package shufit is
   package int_dll is new Ada.Containers.Doubly_Linked_Lists(Element_Type => Integer);
   type int_dll_type is new int_dll.List with null record;
   procedure Foo (List : in out int_dll_type);
end shufit;

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

package shufit_pack is new shufit(element_type => Integer);
a : shufit_pack.int_dll_type;

Попытка 3 - обобщить все

Единственноевещь, о которой я могу думать, это просто бросить все дженерики в функцию, которую мы можем себе представить. На данный момент мы ничего не знаем о задействованном типе, а это значит, что нам нужно указать каждую функцию из duply_linked_list, которую нам нужно использовать.

shufit.ads:

package shufit is
   generic
      type element_type is private;
      type list_type is private;
      with function Length (List : in list_type) return Integer;
   procedure Foo (List : in out list_type);
end shufit;

shufit.adb:

with Ada.Text_IO;

package body shufit is
   procedure Foo (List : in out list_type) is
      i : Integer := Length(List);
   begin
      Ada.Text_IO.Put_Line(Integer'Image(i));
   end Foo;
end shufit;

использование:

package int_dll is new Ada.Containers.Doubly_Linked_Lists(Element_Type => Integer);
type int_dll_type is new int_dll.List with null record;

function IntDLL_Length (List : in int_dll_type) return Integer is
begin
   return Integer(List.Length);
end IntDLL_Length;

procedure shuf_intdll is new shufit.Foo(element_type => Integer, list_type => int_dll_type, Length => IntDLL_Length);

Приятно то, что я могу заставить ту же функцию работать для массивов, что и для duply_linked_lists:

procedure Main
   type IntArray is array (1..10) of Integer;

   function ArrayLength (List : in IntArray) return Integer is
   begin
      return List'Last - List'First + 1;
   end;

   procedure shuf is new shufit.Foo(element_type => Integer, list_type => IntArray, Length => ArrayLength);

   a : IntArray := (others => 5);
begin
   shuf(a);
end Main;

Но это не то, чего я пытаюсь достичь. Я хочу что-то менее громоздкое, что работает для Dubly_linked_lists. При таком подходе вы должны переопределить каждую функцию типа списка, который вы хотите использовать. В примере, который я только что определил, Length, но обычно я хочу, чтобы функция работала на полном интерфейсе Doublely_linked_list, что означает одинаковую написание целой пачки кода, только для различных двухэлементных типов_двойных элементов.

Я просто хочунапишите в Ada эквивалент этих четырех строк C ++:

template<typename T>
void Foo(vector<T> t){
   cout << t.size() << endl;
}

, которые можно использовать для векторов любого типа:

int main(){
  vector<int> a = {0, 1};
  Foo(a);
  return 0;
}

1 Ответ

6 голосов
/ 02 октября 2019

Вы можете попробовать что-то подобное (см. Также RM 12.7 ):

shuffle_list.ads

with Ada.Containers.Doubly_Linked_Lists;

generic
   with package DLL is new Ada.Containers.Doubly_Linked_Lists (<>);
procedure Shuffle_List (List : in out DLL.List);

shuffle_list.adb

with Ada.Containers;      use Ada.Containers;       --  for Count_Type
with GNAT.Random_Numbers; use GNAT.Random_Numbers; 

procedure Shuffle_List (List : in out DLL.List) is
begin   

   if List.Is_Empty then
      return;
   end if;

   --  A poor man's shuffle routine.

   declare

      function Random is
        new Random_Discrete (Count_Type);

      Gen      : Generator;      
      List_New : DLL.List;

   begin

      Reset (Gen);

      while not List.Is_Empty loop
         declare            
            Pos : Count_Type := Random (Gen, 1, List.Length);
            Cur : DLL.Cursor := List.First;            
         begin

            for K in 1 .. Pos - 1 loop
               DLL.Next (Cur);
            end loop;

            --  Move element from one list to the other.
            DLL.Splice
              (Target   => List_New,
               Before   => List_New.First,
               Source   => List,
               Position => Cur);

         end;
      end loop;

      DLL.Move
        (Target => List,
         Source => List_New);

   end;   

end Shuffle_List;

main.adb

with Ada.Text_IO;
with Ada.Containers.Doubly_Linked_Lists;

with Shuffle_List;

procedure Main is

   use Ada.Text_IO;

   package List_Int is
     new Ada.Containers.Doubly_Linked_Lists (Integer);

   procedure Shuffle_List_Int is
     new Shuffle_List (List_Int);

   List : List_Int.List;

begin

   --  Create a list.
   for K in 0 .. 9 loop
      List.Append (K);
   end loop;

   --  Show the list.
   Put_Line ("Input: ");
   for Elem of List loop
      Put (Elem'Image & " ");
   end loop;
   New_Line;

   --  Shuffle the list.
   Shuffle_List_Int (List);

   --  Show the result.
   Put_Line ("Output: ");
   for Elem of List loop
      Put (Elem'Image & " ");
   end loop;
   New_Line;

end Main;

Что дает (в зависимости от состояния генератора случайных чисел):

вывод

Input: 
 0  1  2  3  4  5  6  7  8  9 
Output: 
 5  3  2  8  6  9  1  4  7  0 

Примечание: в своем вопросе вы ссылаетесь как на двусвязный список, так и на эквивалент std::vector. В Аде первая реализована в Ada.Containers.Doubly_Linked_Lists, последняя в Ada.Containers.Vectors.

...