Ada:如何访问向量元素?

smi*_*man 2 pointers ada

我有一个集合,我故意想在堆上分配它们并“通过引用”访问它们:

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;
Run Code Online (Sandbox Code Playgroud)

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

Dee*_*Dee 6

我假设您只想将元素存储在堆上,以便通过引用访问它们以进行操作。但是,使用 Ada 容器时不需要这样做。所有容器都具有某种通过引用访问元素的方式(通过一些Constant_Reference函数Reference,由于Variable_Indexing容器类型上定义的方面,这些函数通常可以被省略;例如,参见Ada 2012 基本原理中的第 6.3 节,和/或@Timur Samkharadze 的回答)。

如果您想将密钥存储为元素的一部分,那么我认为使用散列集可能更合适(请参阅RM A.18.7RM A.18.8learn.adacore.com)。散列集中的元素可以通过函数通过引用来访问Reference_Preserving_Key(另请参见RM 96.10 (3))。

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

主.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;
Run Code Online (Sandbox Code Playgroud)

主.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;
Run Code Online (Sandbox Code Playgroud)

输出(两者相同)

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
Run Code Online (Sandbox Code Playgroud)