Generics 您将如何编写一个Ada泛型函数来操作任何类型元素的双链接列表?

Generics 您将如何编写一个Ada泛型函数来操作任何类型元素的双链接列表?,generics,ada,Generics,Ada,如果可能,我将如何编写一个Ada泛型函数来操作任何类型元素的双链接列表?下面的函数规范通过将Array_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); 因此,在数组的

如果可能,我将如何编写一个Ada泛型函数来操作任何类型元素的双链接列表?下面的函数规范通过将Array_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);
因此,在数组的情况下,您可以精确地泛化实例化中可能不同的内容。然而,尝试用Ada.Containers.Doubly_Linked_List(T.List)进行类比会导致问题

尝试1-实例化双链接列表包的通用过程 这种方法行不通,因为我们无法在泛型部分创建包。即使我们可以这样做,当我们试图调用它时,我们需要在函数定义之外创建一个int_dll_类型并匹配它-这在Ada中似乎不起作用。在C++中,我可以在两个完全不同的命名空间/类中实例化一个“vector”,但它们引用的是同一类型。但是在Ada中,我似乎无法在两个不同的包中引用等价物,没有一个包包含另一个包

尝试2-实例化双链接列表包的通用过程 将元素\u type generic上移到包级别,并修复了类型定义中带有“with null record”后缀的错误“type derived from taged type must have extension”,这会编译,但调用代码必须使用此特定定义的类型,这意味着如果我们还需要在同一类型上运行的Bar过程,不可能单独进行这些操作

package shufit_pack is new shufit(element_type => Integer);
a : shufit_pack.int_dll_type;
尝试3-将所有内容泛化 我唯一能想到的就是把所有的泛型都扔到我们能想象的函数上。此时,我们对所涉及的类型一无所知,这意味着我们需要指定需要使用的双链接列表的每个函数

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);
这样做的好处是,我可以使数组的函数与双链接列表的函数相同:

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;
但这不是我想要实现的。我想要一些不那么麻烦的东西,用于双链接列表。使用这种方法,必须重新定义要使用的列表类型的每个函数。在本例中,我刚刚定义了长度,但通常我希望该函数在双链接列表的完整接口上操作,这意味着编写一整套相同的代码,只针对不同的双链接列表元素类型

我只想用Ada编写这四行C++的等效代码:

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

cout您可以尝试以下方法(另请参见):

洗牌列表。广告

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);
main.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;
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中,第一个是在
Ada.Containers.doubly\u linked\u list
,后者是在
Ada.Containers.Vectors.Vectors

中实现的。你在这个答案中付出的努力给我留下了深刻的印象;你不必实现shuffling算法本身。答案实际上只是要指出,在尝试1中,我需要在包之前使用“with”-以及提供我所需一切的ARM参考。谢谢。我想我将决定使用类似于尝试3的东西,实现在
swaplements上泛化的Knuth Fisher-Yates shuffle算法(list,index1,index2)
函数,然后按照您的建议将其封装在一个函数中,在Ada.Containers.Vectors包上进行泛化;这样我就可以为双链接列表、数组等创建一个函数,同时仍然使用一个版本的KFY shuffle算法。
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);
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;
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