Ada 2020 (8日目) - iterator filter

順調に投稿時刻が後にずれていっております。 なお毎日のリハビリですので書き溜めはしないようにしております。

これまでのあらすじ

Adam Beneschan先生の提案parameterized array component associationはわずかな変更でAdaという言語に大きな影響を与えました。

刺激を受けたTucker Taft先生がaggregate式をもっと発展させようとした結果、どんどん内包表記っぽいことになっております。

Ada 2020でのiterator filter

for ループとaggregate式中のparameterized array component associationに when で条件を書くことができます。 条件を満たさない試行は飛ばされます。

先に例で用いるユーザー定義コンテナを宣言しておきます。

declare
   package Integer_Sets is
     new Ada.Containers.Ordered_Sets (Integer);
   S : constant Integer_Sets.Set := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

for文の場合。 偶数のみ出力します。

begin
   for I in S.Iterate when S (I) rem 2 = 0 loop
      Put (S (I));
   end loop;

勿論 in ではなく of 形式でもフィルタは使えます。 of はAda 2012からの形式で、復習しますと in はインデックス(Cursor、C++で言うイテレータ)をパラメータとするループで of は値そのものをパラメータとして受け取るループです。

begin
   for E of S when E rem 2 = 0 loop
      Put (E);
   end loop;

aggregate式の場合。

declare
  X : Integer_Sets.Set := [for E of S => when E rem 2 = 0];

X には偶数のみが格納されます。

このようにparameterized array component associationでも of は使えます。 これも配列のときに説明しそびれました。 in はnamed association 、 of はpositional associationに対応します。

この例ですと Ada.Containers.Ordered_Sets はnamed associationをサポートしていませんので in で書くことはできません。 Ada.Containers.Ordered_Maps であればキーと値のペアによるnamed associationをサポートしていますので in で書くことができます。

declare
   package Integer_Maps is
     new Ada.Containers.Ordered_Maps (Integer, Integer);
   Y : constant Integer_Maps.Map :=
     [for I in S.Iterate when S (I) rem 2 = 0 use S (I) => S (I) + 1];

Y[2 => 3, 4 => 5, 6 => 7, 8 => 9, 10 => 11] となります。

この短い式で Set から Map へ中身を移せたことにも注目です。 内包表記がマップ関数とフィルタ関数を兼ねることができるのと、同じことができています。

以前インデックスが連続である必要があるため配列ではフィルタは使えません、と書きましたが、補足しておきますとループする対象としては配列は使用できます。 フィルタが使えないのは配列を出力先の型とする場合です。 (違っているかもしれません。もしかしたら others => で抜け番を補えば配列でも使えるかもしれません。) そのため配列からユーザー定義コンテナにマップする場合はフィルタも使えます。

for文でも常に使えます。

begin
   for I in 1 .. 10 when rem 2 = 0 loop
      Put (I);
   end loop;

まあでも上の例もそうですがfor文の場合は続けてif文を書く場合に比べてインデントを1段省略できる程度の意味しかありません。

begin
   for I in 1 .. 10 loop
      if I rem 2 = 0 then
         Put (I);
      end if;
   end loop;

aggregate式で使ってこその機能です。

組み合わせ例

説明しそびれていたこともありましたので、ここまで紹介した機能を組み合わせた例を挙げておきます。

positional association。

[for E of C => Map (E)]

positional association。フィルタ付き。

[for E of C when Filter (E) => Map (E)]

named association。インデックスはそのまま。

[for I in C.Iterate => Map (I)]

named association。インデックスはそのまま。フィルタ付き。

[for I in C.Iterate when Filter (I) => Map (I)]

named association。インデックスも変更。

[for I in C.Iterate use To (I) => Map (I)]

named association。インデックスも変更。フィルタ付き。

[for I in C.Iterate when Filter (I) use To (I) => Map (I)]

関連AI