Ada 202x (6日目) - container aggregate¶
では大物いきますか。 ユーザー定義コンテナのaggregate式です。
経緯¶
これまで、ユーザー定義コンテナを式ひとつで初期化できるケースは限られていました。
主に空の Empty_*
と中身を全部同じ値にする To_*
だけでした。
ですので、配列であればaggregate式で書ける定数でもコンテナに入れようと思えば後から Append
や Insert
等を呼んで要素を足していくしかありませんでした。
declare
package Float_Vectors is new Ada.Containers.Vectors (Positive, Float);
L : Float_Vectors.Vector := Float_Vectors.Empty_Vector (Capacity => 3);
begin
Float_Vectors.Append (L, 1.0);
Float_Vectors.Append (L, 2.0);
Float_Vectors.Append (L, 2.0);
declare
package Float_Maps is new Ada.Containers.Ordered_Maps (Positive, Float);
M : Float_Maps.Map := Float_Maps.Empty_Map;
begin
Float_Maps.Insert (M, 1, 1.0);
Float_Maps.Insert (M, 2, 2.0);
Float_Maps.Insert (M, 3, 2.0);
declare
package Float_Sets is new Ada.Containers.Ordered_Sets (Float);
S : Float_Sets.Set := Float_Sets.Empty_Set;
begin
Float_Sets.Insert (S, 1.0);
Float_Sets.Insert (S, 2.0);
Float_Sets.Insert (S, 3.0);
Ada 202xでの改善¶
コンテナがaggregate式に対応したことで次のように書けます。
Vector
の例。
declare
L : Float_Vectors.Vector := [1 => 1.0, 2 .. 3 => 2.0];
pygments以下略。
Map
の例。
declare
M : Float_Maps.Map := Float_Maps.Empty_Map :=
[for I in 1 .. 2 => Float (I), 3 => 2.0];
Map
の例をもうひとつ。
キーが文字列の場合。
declare
package String_Maps is
new Ada.Containers.Indefinite_Ordered_Maps (String, String);
M : String_Maps.Map :=
["カタナ" => "火迅風魔刀",
"剛剣マンジカブラ" => "秘剣カブラステギ",
"風磨の盾" => "螺旋風魔の盾"];
もしキーがループできない型であってもparameterized array component associationを使うことができます。
配列ではインデックスが常にループできますので説明しそびれました。
use
を用いてキーを指定します。
また予約語の再利用ですね……。
declare
Key : constant array (1 .. 3) of access constant String :=
[new String'("カタナ"),
new String'("剛剣マンジカブラ"),
new String'("風磨の盾")];
Value : constant array (1 .. 3) of access constant String :=
[new String'("火迅風魔刀"),
new String'("秘剣カブラステギ"),
new String'("螺旋風魔の盾")];
package String_Maps is
new Ada.Containers.Indefinite_Ordered_Maps (String, String);
M : String_Maps.Map :=
[for I in 1 .. 3 use Key (I).all => Value (I).all];
Set
の例。
declare
S : Float_Sets.Set := Float_Sets.Empty_Set := [1.0, 2.0, 3.0];
Aggregateアスペクト¶
この機能は標準ライブラリに限らずユーザー定義コンテナ全般で使うことができます。 自前のコンテナで対応したい場合は、当然ながら記述を書き足す必要があります。
Ada 202xでは複数のサブプログラムを組み合わせて実現するようになっています。
型宣言に Aggregate
アスペクトを用いて使用するサブプログラムを指定します。
Vector
の宣言はこうなっています。
type Vector is tagged private
with
-- 他の多数のアスペクトは省略
Aggregate =>
(Empty => Empty,
Add_Unnamed => Append_One,
New_Indexed => New_Vector,
Assign_Indexed => Replace_Element);
type Vector is tagged private
with
-- 他の多数のアスペクトは省略
Aggregate =>
(Empty => Empty,
Add_Unnamed => Append,
New_Indexed => New_Vector,
Assign_Indexed => Replace_Element);
先程の例は
declare
L : Float_Vectors.Vector := [1 => 1.0, 2 .. 3 => 2.0];
このように展開されます。
declare
L : Float_Vectors.Vector := Float_Vectors.New_Vector (1, 3);
begin
Replace_Element (L, 1, 1.0);
for I in 2 .. 3 loop
Replace_Element (L, I, 2.0);
end loop;
配列同様にインデックスを用いる場合のnamed associationは New_Indexed
で確保した範囲に Assign_Indexed
を使って代入していく形に展開されます。
Map
の宣言はこうなっています。
type Map is tagged private
with
-- 他の多数のアスペクトは省略
Aggregate =>
(Empty => Empty,
Add_Named => Insert);
配列同様ではない場合のnamed associationは Empty
に Add_Named
で要素をひとつずつ足していく形に展開されます。
Vector
と異なり *_Indexed
ではありませんが、parameterized array component associationは使用できます。
Set
の宣言はこうなっています。
type Set is tagged private
with
-- 他の多数のアスペクトは省略
Aggregate =>
(Empty => Empty,
Add_Unnamed => Include);
positional associationは Empty
に Add_Unnamed
で要素をひとつずつ足していく形に展開されます。
この3種類が想定されているパターンとなります。
要素の追加に使われている Replace_Element
、 Insert
、 Include
は既存のサブプログラムがそのまま使われています。
Empty
はAda 202xで追加されるものですが、これも既存の Empty_*
で代用可能です。
Empty_*
は定数として宣言されていますので、Capacityを指定できる関数として Empty
が追加されたというだけです。
Vector の Append_One も Length パラメータを取らない版というだけで既存の Append と同じですので、実質 New_Vector
のみがaggregate式のために追加されたものとなります。
勿論 New_Vector
もそれ自体の用途で使用可能です。
(Vector
の場合は用途が思いつかないですが、範囲を与えて空のコンテナを作る関数が有用なケースがあるかもしれません、たぶん。)
このように、aggregate式に対応するために専用のサブプログラムを必要としないようになっています。 似たような機能があるC++ではinitializer_listを受け取るコンストラクタによって実現されています。
Ada.Containers.Vectors.Empty¶
Ada.Containers.Vectors.Append_One¶
Vectors.Append
は要素を追加する版とコンテナの全要素を追加する版でオーバーロードされていて曖昧さの解決のため以前 Append_One
が提案されていました。
現在の提案では Append
をそのまま使用して、代わりにコンテナを追加する版が Append_Vector
として分離されるようです。
関連AI¶
AI12-0212-1 Container aggregates; generalized array aggregates
AI12-0339-1 Empty function for Container aggregates
所感¶
これだけですとまあ構文糖というだけなのですが、Ada 202xではaggregate式に大幅な手が入っておりこれと2日目のparameterized array component associationを元に更に色々できます。 配列にはインデックスが連続でなければならない等の制限がありますので、色々やるならそうした制限を突破できるユーザー定義コンテナでやる必要があります。 ですのでユーザー定義コンテナがaggregate式に対応するのは必須なのかもしれません。 個人的にはAda 2012以降のアスペクトを使ったいかにも後付けっぽい構文糖には、あまり増えてほしくないのですけれどね。