通用函数返回映射

Generic function returning map

在以下示例中,一个有序映射被创建并由函数返回:

with Ada.Containers.Ordered_Maps;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

procedure Main is
   package My_Map is new Ada.Containers.Ordered_Maps
     (Key_Type     => Natural,
      Element_Type => Unbounded_String);

   function Create_Map return My_Map.Map is
      Map_Instance : My_Map.Map;
   begin
      Map_Instance.Insert (1, To_Unbounded_String ("Foo"));
      Map_Instance.Insert (2, To_Unbounded_String ("Bar"));

      return Map_Instance;
   end Create_Map;
begin
   null;
end Main;

我想知道是否可以将函数 Create_Map 转换为更通用的函数?下面的代码展示了我的想法,但不幸的是我真的不知道正确的形式类型 ???:

   generic
      type Key_Type is ???;
      type Element_Type is ???;
      type Map_Type is ???;
   function Create_Map_Generic return Map_Type is
      Map_Instance : Map_Type;
   begin
      -- Inserts calls would be based on parsing data ...

      return Map_Instance;
   end;

   package My_Map_One is new Ada.Containers.Ordered_Maps
     (Key_Type     => Natural,
      Element_Type => Unbounded_String);

   function Create_Map_One is new Create_Map_Generic
     (Key_Type     => Natural,
      Element_Type => Unbounded_String,
      Map_Type     => My_Map_One.Map);

   package My_Map_Two is new Ada.Containers.Ordered_Maps
     (Key_Type     => Unbounded_String,
      Element_Type => Positive);

   function Create_Map_Two is new Create_Map_Generic
     (Key_Type     => Unbounded_String,
      Element_Type => Positive,
      Map_Type     => My_Map_Two.Map);

正式类型 Key_TypeElement_Type 指定了两次(包声明和泛型函数的实例化)。这些可以从包本身的声明中提取吗?

我不确定这是否能回答您的用例,但这里是..

我不认为你可以只用一个通用函数来做到这一点;我认为最好是作为一个通用包(声明地图,从例如 PositiveUnbounded_String)包含一个通用函数以使用特定的人口子程序创建地图。

外泛型:

with Ada.Containers.Ordered_Maps;
generic
   type Key_Type is private;
   type Element_Type is private;
   with function "<" (Left, Right : Key_Type) return Boolean is <>;
   with function "=" (Left, Right : Element_Type) return Boolean is <>;
package Maps_G is
   package Maps is new Ada.Containers.Ordered_Maps
     (Key_Type     => Key_Type,
      Element_Type => Element_Type,
      "<"          => "<",
      "="          => "=");
   subtype Map is Maps.Map;
   generic
      with procedure Populate (The_Map : in out Map);
   function Create_Map return Map;
end Maps_G;

及其正文:

package body Maps_G is
   function Create_Map return Map is
   begin
      return M : Map do
         Populate (M);
      end return;
   end Create_Map;
end Maps_G;

加上一点测试程序:

with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Maps_G;
with Ada.Text_IO;
procedure Test is
   package P_To_UBS_Maps is new Maps_G (Key_Type     => Positive,
                                        Element_Type => Unbounded_String);
   procedure Populate (The_Map : in out P_To_UBS_Maps.Map) is
   begin
      The_Map.Insert (42, To_Unbounded_String ("the answer"));
      The_Map.Insert (1, To_Unbounded_String ("one"));
   end Populate;
   function Create
     is new P_To_UBS_Maps.Create_Map (Populate);
   M : P_To_UBS_Maps.Map := Create;
begin
   for J in M.Iterate loop
      Ada.Text_IO.Put_Line (P_To_UBS_Maps.Maps.Key (J)'Image
                              & " -> "
                              & To_String (P_To_UBS_Maps.Maps.Element (J)));
   end loop;
end Test;

构建 & 运行:

$ gnatmake test -f -g
gcc -c -g test.adb
gcc -c -g maps_g.adb
gnatbind -x test.ali
gnatlink test.ali -g
gnatlink: warning: executable name "test" may conflict with shell command
$ ./test
 1 -> one
 42 -> the answer

完成了,与直接调用 Populate 相比似乎没有真正的优势。

西蒙给出了更好更完整的答案。我只是提供一个替代方案,将地图包作为形式参数传入。为此,您可以将包作为形式参数传递,只要它的参数也作为形式参数发送:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Ordered_Maps;

procedure Hello is

    generic
        type Key_Type is private;
        type Element_Type is private;
        with function "<"(L,R : Key_Type) return Boolean is <>;
        with function "="(L,R : Key_Type) return Boolean is <>;

        with package Map_Package is new Ada.Containers.Ordered_Maps
            (Key_Type     => Key_Type,
             Element_Type => Element_Type,
             "<"          => "<",
             "="          => "=");
    function Create_Map_Generic return Map_Package.Map;        

    function Create_Map_Generic return Map_Package.Map is
    begin
        return Result : Map_Package.Map do
            null;  -- replace with your input based creation
        end return;
    end Create_Map_Generic;

    -- Just a nonsense type to test out compilation
    type My_Key is null record;
    function "<"(L,R : My_Key) return Boolean is (True);
    function "="(L,R : My_Key) return Boolean is (True);

    package Maps is new Ada.Containers.Ordered_Maps
        (Key_Type     => My_Key,
         Element_Type => Integer);

    function Create is new Create_Map_Generic
        (Key_Type     => My_Key,
         Element_Type => Integer,
         Map_Package  => Maps);

begin
  Put_Line("Hello, world!");
end Hello;

作为额外的花絮,如果您不关心函数内部的键和元素类型是什么(我想您关心,但以防万一),那么您不必指定它们,只需简单地做:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Ordered_Maps;

procedure Hello is

    generic
        with package Map_Package is new Ada.Containers.Ordered_Maps
            (others => <>);
    function Create_Map_Generic return Map_Package.Map;        

    function Create_Map_Generic return Map_Package.Map is
    begin
        return Result : Map_Package.Map do
            null;  -- replace with your input based creation
        end return;
    end Create_Map_Generic;

    -- Just a nonsense type to test out compilation
    type My_Key is null record;
    function "<"(L,R : My_Key) return Boolean is (True);
    function "="(L,R : My_Key) return Boolean is (True);

    package Maps is new Ada.Containers.Ordered_Maps
        (Key_Type     => My_Key,
         Element_Type => Integer);

    function Create is new Create_Map_Generic
        (Map_Package  => Maps);

begin
  Put_Line("Hello, world!");
end Hello;

你也可以在中间折衷,这取决于你想在你的创建函数中使用什么:

generic
    type Key_Type is private;
    type Element_Type is private;
    with package Map_Package is new Ada.Containers.Ordered_Maps
        (Key_Type     => Key_Type,
         Element_Type => Element_Type,
         others       => <>);
function Create_Map_Generic return Map_Package.Map;