【问题标题】:Ada: How to get Access to Vector element?Ada:如何访问 Vector 元素?
【发布时间】:2021-02-20 13:14:45
【问题描述】:

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

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;

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

【问题讨论】:

  • 问题是你为什么要首先在这里处理指针?特别是因为您在堆上分配,然后在将数据插入地图时制作数据副本。
  • @egilhh 这是一个简化的例子,在现实世界中,“事物”可以很大(并且永远存在)。另外,程序会不断更新'Thing'的内容,Maps返回的是对象的副本,而不是对象本身。
  • 你好像没有向量。
  • @smirkingman 是的,我确实删除了答案。答案被某个在与 Ada 相关的答案中不常见的人投票否决。我想有人真的被这个答案困扰了。不幸的是,我不知道问题是什么。也许我对使用Hashed_Set 而不是“Hashed_Map”的建议过于宽松,因为我不知道确切的用例,也许答案不够中肯,或者我真的做了一个我的例子中的错误。无论如何,帝汶的回答已经很好,所以我决定将其删除以确保安全。我把它放回去让你再看看。
  • 大概投反对票的人会读到:你的反对​​票导致 DeeDee 删除了他的答案,这是我无法单独解决的问题的解决方案。幸运的是,我在删除之前看到它并记住了 DeeDee 的名字,然后我浪费了相当长的时间来恢复答案。将来,当对有说服力的答案投反对票时,请体面地添加评论以解释原因。

标签: pointers ada


【解决方案1】:

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

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

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

ma​​in.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;

ma​​in.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

【讨论】:

  • 很好的答案,我不会在周日的一个月内找到 Reference_Preserving_Key,谢谢。您能否添加一些示例来说明如何使用您提到的“参考”和“Constant_Reference”? (我会接受 ;-)
  • @smirkingman 我更新了原始版本并添加了一个额外的示例来显示ReferenceConstant_Reference 的使用。我希望它有用。
【解决方案2】:

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

函数Reference返回一个值Reference_Type,它具有Implicit_Dereference,其值为Element,其类型为not null access Element_Type

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-18
    • 1970-01-01
    • 2013-03-22
    • 2012-12-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多