Ada 202x (6日目) - container aggregate ====================================== .. post:: Dec 06, 2019 :tags: ada, ada_2022 では大物いきますか。 ユーザー定義コンテナのaggregate式です。 経緯 ---- これまで、ユーザー定義コンテナを式ひとつで初期化できるケースは限られていました。 主に空の ``Empty_*`` と中身を全部同じ値にする ``To_*`` だけでした。 ですので、配列であればaggregate式で書ける定数でもコンテナに入れようと思えば後から ``Append`` や ``Insert`` 等を呼んで要素を足していくしかありませんでした。 .. code-block:: ada 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); .. code-block:: ada 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); .. code-block:: ada 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`` の例。 .. code-block:: ada declare L : Float_Vectors.Vector := [1 => 1.0, 2 .. 3 => 2.0]; .. container:: gray-and-small pygments以下略。 ``Map`` の例。 .. code-block:: ada declare M : Float_Maps.Map := Float_Maps.Empty_Map := [for I in 1 .. 2 => Float (I), 3 => 2.0]; ``Map`` の例をもうひとつ。 キーが文字列の場合。 .. code-block:: ada declare package String_Maps is new Ada.Containers.Indefinite_Ordered_Maps (String, String); M : String_Maps.Map := ["カタナ" => "火迅風魔刀", "剛剣マンジカブラ" => "秘剣カブラステギ", "風磨の盾" => "螺旋風魔の盾"]; もしキーがループできない型であってもparameterized array component associationを使うことができます。 配列ではインデックスが常にループできますので説明しそびれました。 ``use`` を用いてキーを指定します。 また予約語の再利用ですね……。 .. code-block:: ada 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`` の例。 .. code-block:: ada declare S : Float_Sets.Set := Float_Sets.Empty_Set := [1.0, 2.0, 3.0]; Aggregateアスペクト +++++++++++++++++++ この機能は標準ライブラリに限らずユーザー定義コンテナ全般で使うことができます。 自前のコンテナで対応したい場合は、当然ながら記述を書き足す必要があります。 Ada 202xでは複数のサブプログラムを組み合わせて実現するようになっています。 型宣言に ``Aggregate`` アスペクトを用いて使用するサブプログラムを指定します。 ``Vector`` の宣言はこうなっています。 .. container:: strike .. code-block:: ada type Vector is tagged private with -- 他の多数のアスペクトは省略 Aggregate => (Empty => Empty, Add_Unnamed => Append_One, New_Indexed => New_Vector, Assign_Indexed => Replace_Element); .. code-block:: ada type Vector is tagged private with -- 他の多数のアスペクトは省略 Aggregate => (Empty => Empty, Add_Unnamed => Append, New_Indexed => New_Vector, Assign_Indexed => Replace_Element); 先程の例は .. code-block:: ada declare L : Float_Vectors.Vector := [1 => 1.0, 2 .. 3 => 2.0]; このように展開されます。 .. code-block:: ada 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`` の宣言はこうなっています。 .. code-block:: ada type Map is tagged private with -- 他の多数のアスペクトは省略 Aggregate => (Empty => Empty, Add_Named => Insert); 配列同様ではない場合のnamed associationは ``Empty`` に ``Add_Named`` で要素をひとつずつ足していく形に展開されます。 ``Vector`` と異なり ``*_Indexed`` ではありませんが、parameterized array component associationは使用できます。 ``Set`` の宣言はこうなっています。 .. code-block:: ada type Set is tagged private with -- 他の多数のアスペクトは省略 Aggregate => (Empty => Empty, Add_Unnamed => Include); positional associationは ``Empty`` に ``Add_Unnamed`` で要素をひとつずつ足していく形に展開されます。 この3種類が想定されているパターンとなります。 .. role:: strike 要素の追加に使われている ``Replace_Element`` 、 ``Insert`` 、 ``Include`` は既存のサブプログラムがそのまま使われています。 ``Empty`` はAda 202xで追加されるものですが、これも既存の ``Empty_*`` で代用可能です。 ``Empty_*`` は定数として宣言されていますので、Capacityを指定できる関数として ``Empty`` が追加されたというだけです。 :strike:`Vector の Append_One も Length パラメータを取らない版というだけで既存の Append と同じですので、`\ 実質 ``New_Vector`` のみがaggregate式のために追加されたものとなります。 勿論 ``New_Vector`` もそれ自体の用途で使用可能です。 (``Vector`` の場合は用途が思いつかないですが、範囲を与えて空のコンテナを作る関数が有用なケースがあるかもしれません、たぶん。) このように、aggregate式に対応するために専用のサブプログラムを必要としないようになっています。 似たような機能があるC++ではinitializer_listを受け取るコンストラクタによって実現されています。 Ada.Containers.Vectors.Empty ++++++++++++++++++++++++++++ | http://www.ada-auth.org/standards/2xrm/html/RM-A-18-2.html#p12.4 Ada.Containers.Vectors.Append_One +++++++++++++++++++++++++++++++++ .. container:: strike | http://www.ada-auth.org/standards/2xrm/html/RM-A-18-2.html#p47.1 .. container:: inserted ``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以降のアスペクトを使ったいかにも後付けっぽい構文糖には、あまり増えてほしくないのですけれどね。 .. _`AI12-0212-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0212-1.txt .. _`AI12-0339-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0339-1.txt