Ada 2020 (2日目) - parameterized array component associations

というわけで、一番お気に入りの新機能から行きます。 説明を考えたら順番はgeneralized array aggregateの後の方がいいのですが、元々人に読ませることは考えていないので好きな順で。

経緯

配列の個々の要素を個別の値で初期化したいとします。 固定長ならそのまま書き下せばいいです。

declare
   Array_Var : array (1 .. 3) of T :=
     (1 => Create (1), 2 => Create (2), 3 => Create (3));
declare
   Geometric : constant array (0 .. 4) of Integer := (1, 2, 4, 8, 16);
declare
   Identity_Matrix : constant array (1 .. 3, 1 .. 3) of Float :=
     ((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0));

これが可変長になりますと、後から初期化するしかなかったわけです。

declare
   Array_Var : array (1 .. Length) of T;
begin
   for I in Array_Var'Range loop
      Array_Var (I) := Create (I);
   end loop;
declare
   Geometric : array (0 .. Length) of Integer;
begin
   for I in Geometric'Range loop
      Geometric (I) := 2 ** I;
   end loop;
declare
   Identity_Matrix : array (1 .. Length, 1 .. Length) of Float :=
     (others => (others => 0.0));
begin
   for I in 1 .. Length loop
      Identity_Matrix (I, I) := 1.0;
   end loop;

constant も外さざるを得ませんし、もし limited 型や task 型、discriminantsを持つ型などの場合は後から初期化することができないこともあります。 taskbegin の時点で起動されてしまいます。

Ada 2020での改善

Adam Beneschan先生の単純にして絶大な大発明。 ちなみに元Irvine Compiler Corporation所属でICC/Adaの開発者様。最近Amazonに転職されたらしい……。

まず上記例がどうなるのかを。

declare
   Array_Var : array (1 .. Length) of T :=
     (for I in 1 .. Length => Create (I));
declare
   Geometric : constant array (0 .. Length) of Integer :=
     (for I in 0 .. Length => 2 ** I);
declare
   Identity_Matrix : constant array (1 .. Length, 1 .. Length) of Float :=
     (for I in 1 .. Length => (I => 1.0, others => 0.0));

構文としてはインデックス付きaggregate式の 範囲 => の手前に for I in が挿入された形です。 そしてインデックス I を値として使うことができます。

それだけですが、それによってできるようになった事の幅がぱねぇです。 それでいて元からあった機能のように違和感もなく。 拡張はこういうふうにやるものだという見本みたいな。

関連AI

対比

配列の初期化式にインデックスを書くことができる他の言語としては、C言語があります。

int Array_Var[3] = {[0] = Create(1), [1] = Create(2), [2] = Create(3)};

この [0][1][2] として書いているインデックスには、わかりやすさと、ソースコード上での順番を入れ替えることができる程度の意味しかありません。 そもそもインデックスの部分に範囲を書けませんので、可変長配列であってもソースコードに書いた固定個数から後はゼロ初期化に任せるしかないです。

更に

こうなると内包表記に発展させたくなるのが人情で、 when でフィルターを書けるようにする提案も採択されています。 ただ配列の場合はインデックスが連続でないといけませんので、ユーザー定義コンテナ向けの機能になります。 そちらはまた後日。 (ネタを使い切らないよう小出し)

所感

定数や limited 型の初期化はもちろん、引数として配列を渡すときもインラインで書けるケースが大幅に増加したわけで、すごく嬉しい機能です。

静的型の言語で、こうやって複雑な配列定数を一発で書けるものは無いのではないでしょうか。 嘘です。 Haskellのunboxed arrayが静的に展開されるならあっちが上でしょうし(されないと思ってます)、D言語のCTFEや、nimやRustのようなマクロの強力なやつならどうにでもなりそうです。 ただ無駄な過程を経ず値を組み立てられるかとなりますと、コンパイル時placement new的なものも必要になると思われますので中々難しいのでは。 挑戦しても面白いかもです。