Ada:如何迭代私有地图?
Ada: How to Iterate over a private map?
考虑以下几点:
with Ada.Containers.Hashed_Maps;
with Ada.Containers; use Ada.Containers;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
package Tiles is
-- Implementation is completely hidden
type Tile_Type is private;
type Tile_Set is tagged private;
type Tile_Key is private;
procedure Add (Collection : in out Tile_Set; Tile : Tile_Type);
function Get (Collection : in Tile_Set; Key : Natural) return Tile_Type;
function Make (Key : Natural; Data : Integer) return Tile_Type;
function Image (Tile : Tile_Type) return String;
private
type Tile_Key is record
X : Natural;
end record;
function Tile_Hash (K : Tile_Key) return Hash_Type is
(Hash_Type (K.X));
type Tile_Type is record
Key : Tile_Key;
Data : Integer;
end record;
package Tile_Matrix is new Ada.Containers.Hashed_Maps
(Element_Type => Tile_Type,
Key_Type => Tile_Key,
Hash => Tile_Hash,
Equivalent_Keys => "=");
use Tile_Matrix;
type Tile_Set is new Tile_Matrix.Map with null record;
end Tiles;
package body Tiles is
procedure Add (Collection : in out Tile_Set; Tile : Tile_Type) is
begin
Collection.Include (Key => Tile.Key, New_Item => Tile);
end Add;
function Get (Collection : in Tile_Set; Key : Natural) return Tile_Type is
K : Tile_Key := (X => Key);
C : Cursor := Collection.Find (Key => K);
begin -- For illustration, would need to handle missing keys
return Result : Tile_Type do
Result := Collection (C);
end return;
end Get;
function Image (Tile : Tile_Type) return String is
(Tile.Key.X'Image & '=' & Tile.Data'Image);
function Make (Key : Natural; Data : Integer) return Tile_Type is
New_Key : Tile_Key := (X => Key);
begin
return Result : Tile_Type do
Result.Key := New_Key;
Result.Data := Data;
end return;
end Make;
end Tiles;
use Tiles;
S : Tile_Set;
T : Tile_Type;
begin
S.Add (Make (Key => 1, Data => 10));
T := S.Get (1);
Put_Line (Image (T)); -- 1, 10
S.Add (Make (Key => 2, Data => 20));
T := S.Get (2);
Put_Line (Image (T)); -- 1, 20
for X in S loop -- Fails: cannot iterate over "Tile_Set"
-- +: to iterate directly over the elements of a container, write "of S"
-- but "for X of S" doesn't work either.
T := S (X); -- Fails: array type required in indexed component
-- presumably because X isn't a cursor?
Put_Line (Image (T));
end loop;
end;
在我看来,编译器有足够的知识来迭代 Tile_Set,我想它不会,因为我没有公开迭代器。
我该如何修改才能使 'for X is S loop' 有效?
更一般地说,隐藏底层容器的实现,同时公开索引、迭代等的习惯用法是什么?
It seems to me that the compiler has enough knowledge to iterate over a Tile_Set and I'm supposing it won't because I haven't exposed an iterator.
该评估是正确的。为了能够循环一个类型,该类型需要定义 Default_Iterator
和 Iterator_Element
方面,如 LRM 5.5.1, and the aspect Constant_Indexing
as described in LRM 4.1.6 中所述。两节阅读
These aspects are inherited by descendants of type T (including T'Class).
这意味着由于 Tile_Set
继承自 Tile_Matrix.Map
,它确实继承了该地图上定义的这些方面。但是,由于继承关系是私有的,所以方面在该包之外是不可见的。
您也不能将它们显式设置为私有类型,因为 4.1.6 说
The aspects shall not be overridden, but the functions they denote may be.
将它们设置为私有类型会覆盖私有部分中继承的方面。
你有两个选择:
- 创建继承关系public以便您可以立即访问所有方面。
- 使
Tile_Set
封装Hashed_Map
值,这样你就可以在类型上实现自己的迭代。
第二个选项如下所示:
type Cursor is private;
type Tile_Set is private
with Default_Iterator => Iterate,
Iterator_Element => Tile_Type,
Constant_Indexing => Constant_Reference;
function Has_Element (Position: Cursor) return Boolean;
package Tile_Set_Iterator_Interfaces is new
Ada.Iterator_Interfaces (Cursor, Has_Element);
type Constant_Reference_Type
(Element : not null access constant Tile_Type) is private
with Implicit_Dereference => Element;
function Iterate (Container: in Tile_Set) return
Tile_Set_Iterator_Interfaces.Forward_Iterator'Class;
function Constant_Reference (Container : aliased in Tile_Set;
Position : Cursor)
return Constant_Reference_Type;
private
-- ..
type Cursor is record
Data : Tile_Matrix.Cursor;
end record;
type Tile_Set is record
Data : Tile_Matrix.Map;
end record;
在这些子程序的实现中,您可以简单地委托给Tile_Matrix
个子程序。
教训是当你的实际意图是组合时你不应该继承。
考虑以下几点:
with Ada.Containers.Hashed_Maps;
with Ada.Containers; use Ada.Containers;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
package Tiles is
-- Implementation is completely hidden
type Tile_Type is private;
type Tile_Set is tagged private;
type Tile_Key is private;
procedure Add (Collection : in out Tile_Set; Tile : Tile_Type);
function Get (Collection : in Tile_Set; Key : Natural) return Tile_Type;
function Make (Key : Natural; Data : Integer) return Tile_Type;
function Image (Tile : Tile_Type) return String;
private
type Tile_Key is record
X : Natural;
end record;
function Tile_Hash (K : Tile_Key) return Hash_Type is
(Hash_Type (K.X));
type Tile_Type is record
Key : Tile_Key;
Data : Integer;
end record;
package Tile_Matrix is new Ada.Containers.Hashed_Maps
(Element_Type => Tile_Type,
Key_Type => Tile_Key,
Hash => Tile_Hash,
Equivalent_Keys => "=");
use Tile_Matrix;
type Tile_Set is new Tile_Matrix.Map with null record;
end Tiles;
package body Tiles is
procedure Add (Collection : in out Tile_Set; Tile : Tile_Type) is
begin
Collection.Include (Key => Tile.Key, New_Item => Tile);
end Add;
function Get (Collection : in Tile_Set; Key : Natural) return Tile_Type is
K : Tile_Key := (X => Key);
C : Cursor := Collection.Find (Key => K);
begin -- For illustration, would need to handle missing keys
return Result : Tile_Type do
Result := Collection (C);
end return;
end Get;
function Image (Tile : Tile_Type) return String is
(Tile.Key.X'Image & '=' & Tile.Data'Image);
function Make (Key : Natural; Data : Integer) return Tile_Type is
New_Key : Tile_Key := (X => Key);
begin
return Result : Tile_Type do
Result.Key := New_Key;
Result.Data := Data;
end return;
end Make;
end Tiles;
use Tiles;
S : Tile_Set;
T : Tile_Type;
begin
S.Add (Make (Key => 1, Data => 10));
T := S.Get (1);
Put_Line (Image (T)); -- 1, 10
S.Add (Make (Key => 2, Data => 20));
T := S.Get (2);
Put_Line (Image (T)); -- 1, 20
for X in S loop -- Fails: cannot iterate over "Tile_Set"
-- +: to iterate directly over the elements of a container, write "of S"
-- but "for X of S" doesn't work either.
T := S (X); -- Fails: array type required in indexed component
-- presumably because X isn't a cursor?
Put_Line (Image (T));
end loop;
end;
在我看来,编译器有足够的知识来迭代 Tile_Set,我想它不会,因为我没有公开迭代器。
我该如何修改才能使 'for X is S loop' 有效?
更一般地说,隐藏底层容器的实现,同时公开索引、迭代等的习惯用法是什么?
It seems to me that the compiler has enough knowledge to iterate over a Tile_Set and I'm supposing it won't because I haven't exposed an iterator.
该评估是正确的。为了能够循环一个类型,该类型需要定义 Default_Iterator
和 Iterator_Element
方面,如 LRM 5.5.1, and the aspect Constant_Indexing
as described in LRM 4.1.6 中所述。两节阅读
These aspects are inherited by descendants of type T (including T'Class).
这意味着由于 Tile_Set
继承自 Tile_Matrix.Map
,它确实继承了该地图上定义的这些方面。但是,由于继承关系是私有的,所以方面在该包之外是不可见的。
您也不能将它们显式设置为私有类型,因为 4.1.6 说
The aspects shall not be overridden, but the functions they denote may be.
将它们设置为私有类型会覆盖私有部分中继承的方面。
你有两个选择:
- 创建继承关系public以便您可以立即访问所有方面。
- 使
Tile_Set
封装Hashed_Map
值,这样你就可以在类型上实现自己的迭代。
第二个选项如下所示:
type Cursor is private;
type Tile_Set is private
with Default_Iterator => Iterate,
Iterator_Element => Tile_Type,
Constant_Indexing => Constant_Reference;
function Has_Element (Position: Cursor) return Boolean;
package Tile_Set_Iterator_Interfaces is new
Ada.Iterator_Interfaces (Cursor, Has_Element);
type Constant_Reference_Type
(Element : not null access constant Tile_Type) is private
with Implicit_Dereference => Element;
function Iterate (Container: in Tile_Set) return
Tile_Set_Iterator_Interfaces.Forward_Iterator'Class;
function Constant_Reference (Container : aliased in Tile_Set;
Position : Cursor)
return Constant_Reference_Type;
private
-- ..
type Cursor is record
Data : Tile_Matrix.Cursor;
end record;
type Tile_Set is record
Data : Tile_Matrix.Map;
end record;
在这些子程序的实现中,您可以简单地委托给Tile_Matrix
个子程序。
教训是当你的实际意图是组合时你不应该继承。