Ada:如何访问 Vector 元素?

Ada: How to get Access to Vector element?

我有一个东西的集合,我特意想在堆上分配并访问它们'by reference':

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Indefinite_Hashed_Maps;
with Ada.Containers; use Ada.Containers;

procedure Main is
    
    type Thing_Key is new Integer;
    
    type Thing is record
        Key  : Thing_Key;
        Data : Integer;
    end record;
    
    type Thing_Access is access all Thing;
    
    function Image (T : Thing) return String is
      (T.Key'Image & '(' & T.Data'Image & ')');
    
    function "=" (A, B : Thing) return Boolean is
      (A.Key = B.Key);
    
    function Thing_Hash (K : Thing_Key) return Hash_Type is
      (Hash_Type (K));

    package Thing_Map is new
      Ada.Containers.Indefinite_Hashed_Maps
         (Key_Type        => Thing_Key,
     Element_Type    => Thing,
     Hash            => Thing_Hash,
     Equivalent_Keys => "=");
    use Thing_Map;
    
    Map : Thing_Map.Map;
    C : Cursor;
    P : Thing_Access;
begin
    P := new Thing '(Key => 1, Data => 2);  -- on the heap
    
    Map.Insert (P.Key, P.all);
    Put_Line (Image (P.all)); -- '1( 2)', as expected

    P.Data := 99;
    Put_Line (Image (P.all)); -- '1( 99)', as expected

    C := Map.Find (1); -- Get cursor to thing
    
    -- Set P to point at the thing at the cursor?
    
    -- Following lines don't compile
    P := Map (C)'Access; -- access-to-variable designates constant
    P := Map (C).Reference; -- undefined selector "Reference" for overloaded prefix
    P := Map (C).Get_Element_Access; -- undefined selector "Get_Element_Access" for overloaded prefix
    P := Map.Reference (C); -- no visible interpretation of "Reference" matches expected type "Thing_Access"
end Main;

从游标获取指针的语法是什么?

您可能想要使用 P := Map.Reference(C).Element;

函数 Reference returns Reference_Type 的值,其方面 Implicit_Dereference 的值为 Element,类型为 not null access Element_Type

我假设您只想将元素存储在堆上,以便通过引用访问它们以进行操作。但是,使用 Ada 容器时不需要这样做。所有容器都有一些通过引用访问元素的方式(通过一些 Constant_ReferenceReference 函数,通常可以省略,因为在容器类型上定义了 Variable_Indexing 方面;见,例如,section 6.3 在 Ada 2012 基本原理中,and/or @Timur Samkharadze 的答案)。

如果要将键存储为元素的一部分,那么我认为使用散列集可能更合适(参见 RM A.18.7, RM A.18.8 and on learn.adacore.com). An element in a hashed set can be accessed by reference via the function Reference_Preserving_Key (see also RM 96.10 (3))。

下面是两个例子:第一个例子展示了如何更新 Hashed_Map 中的元素,第二个例子展示了如何更新 Hashed_Set 中的元素,两者都使用一个键:

main.adb (Hashed_Map)

with Ada.Text_IO;    use Ada.Text_IO;
with Ada.Containers; use Ada.Containers;
with Ada.Containers.Hashed_Maps;

procedure Main is

   type Thing_Key is new Integer;

   type Thing is record
      Key  : Thing_Key;
      Data : Integer;
   end record;

   function Image (T : Thing) return String is
     ("Key = " & T.Key'Image & ", Value = " & T.Data'Image);

   function Hash (K : Thing_Key) return Hash_Type is (Hash_Type (K));

   package Things is new Ada.Containers.Hashed_Maps
     (Key_Type       => Thing_Key,
      Element_Type   => Thing,
      Hash           => Hash,
     Equivalent_Keys => "=");

   Map : Things.Map;

begin
   
   --  Inserting 4 elements.  Note that the key is now stored twice: once in
   --  the map's key index (its hash, to be more precise), and once in the item
   --  itself (unhashed).  You must now ensure that the key value in the
   --  element does not accidentally get out-of-sync with the hashed key in the
   --  map's key index (e.g. when you update the stored element).  Of course,
   --  you could also you just omit the key in the element itself if possible
   --  given your use-case.
   
   Map.Insert (Key => 1, New_Item => (Key => 1, Data => 10));
   Map.Insert (Key => 2, New_Item => (Key => 2, Data => 20));
   Map.Insert (Key => 3, New_Item => (Key => 3, Data => 30));
   Map.Insert (Key => 4, New_Item => (Key => 4, Data => 40));

   for T of Map loop
      Put_Line (Image (T));
   end loop;
   New_Line;
   
   --  Update element with key 3.
   --
   --  Note that the following expressions are all equivalent:
   --
   --     Map.Reference (3).Element.all.Data := 300;   --  Original expression
   --     Map.Reference (3).Element.Data := 300;       --  Omit "all" due to implicit dereferencing of access types in Ada.
   --     Map.Reference (3).Data := 300;               --  Omit "Element" due to the "Implicit_Dereferencing" aspect on the "Hashed_Maps.Reference_Type".
   --     Map (3).Data := 300;                         --  Omit "Reference" due to the "Variable_Indexing" aspect on the "Hashed_Maps.Map" type.
   --
   Map (3).Data := 300;   

   --  Example if you really need a pointer to element with key 3.
   declare
            
      type Thing_Access is not null access all Thing;
      type Thing_Constant_Access is not null access constant Thing;      
      
      --  Element is     mutable via P , i.e.  P.Data := 301 (OK)
      --  Element is not mutable via CP, i.e. CP.Data := 302 (Error)
      
      P  : Thing_Access          := Map.Reference (3).Element;
      CP : Thing_Constant_Access := Map.Constant_Reference (3).Element;
      
   begin
      null;
   end;

   for T of Map loop
      Put_Line (Image (T));
   end loop;
   New_Line;

end Main;

main.adb (Hashed_Set)

with Ada.Text_IO;    use Ada.Text_IO;
with Ada.Containers; use Ada.Containers;
with Ada.Containers.Hashed_Sets;

procedure Main is

   type Thing_Key is new Integer;

   type Thing is record
      Key  : Thing_Key;
      Data : Integer;
   end record;

   function Image (T : Thing) return String is
     ("Key = " & T.Key'Image & ", Value = " & T.Data'Image);

   function Key  (T : Thing)     return Thing_Key is (T.Key);
   function Hash (T : Thing)     return Hash_Type is (Hash_Type (T.Key));
   function Hash (K : Thing_Key) return Hash_Type is (Hash_Type (K));

   package Things is new Ada.Containers.Hashed_Sets
     (Element_Type        => Thing,
      Hash                => Hash,
      Equivalent_Elements => "=");

   package Things_Keys is new Things.Generic_Keys
     (Key_Type        => Thing_Key,
      Key             => Key,
      Hash            => Hash,
      Equivalent_Keys => "=");

   Set : Things.Set;

begin
   
   --  Inserting 4 elements.  Note that the key is stored only in the element.
   
   Set.Insert ((Key => 1, Data => 10));
   Set.Insert ((Key => 2, Data => 20));
   Set.Insert ((Key => 3, Data => 30));
   Set.Insert ((Key => 4, Data => 40));

   for T of Set loop
      Put_Line (Image (T));
   end loop;
   New_Line;

   --  Update the element.  See also RM 96.10 (3).  Opposed to most other
   --  containers, you cannot omit "Reference_Preserving_Key" as the "Set" type
   --  does not have a "Variable_Indexing" aspect specifying "Reference_Preserving_Key".
   --  Hence, you need write it out explicitly.   
   
   Things_Keys.Reference_Preserving_Key (Set, 3).Data := 300;

   --  Example if you really need a pointer to element with key 3.
   declare
            
      type Thing_Access is not null access all Thing;
      type Thing_Constant_Access is not null access constant Thing;      
      
      --  Element is     mutable via P , i.e.  P.Data := 301 (OK)
      --  Element is not mutable via CP, i.e. CP.Data := 302 (Error)
      
      P  : Thing_Access          := Things_Keys.Reference_Preserving_Key (Set, 3).Element;  
      CP : Thing_Constant_Access := Things_Keys.Constant_Reference (Set, 3).Element;
      
   begin
      null;
   end;

   for T of Set loop
      Put_Line (Image (T));
   end loop;
   New_Line;

end Main;

输出(两者相同)

Key =  1, Value =  10
Key =  2, Value =  20
Key =  3, Value =  30
Key =  4, Value =  40

Key =  1, Value =  10
Key =  2, Value =  20
Key =  3, Value =  300
Key =  4, Value =  40