イテレータ

Iterator に抵抗があるなら、自分の時代遅れを見直した方が良いです。
http://twitter.com/shinji_kono/status/3317315646

……と言われたので、イテレータについて調べてみました。

プログラミング言語において配列やそれに類似するデータ構造の各要素に対する繰返し処理の抽象化である。
http://ja.wikipedia.org/wiki/%e3%82%a4%e3%83%86%e3%83%ac%e3%83%bc%e3%82%bf

一般には、Rubyのやつと、C++のやつが幅を効かせている様ですが、結構それ以外もあります。節操の無い言語は複数種類用意してますね。
それで、節操の無いAdaは、更にもう1種類追加しようとしているわけです。

http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai05s/ai05-0139-1.txt

これには、今あるループ方法が果てしなく面倒くさいという理由があります。

外部イテレータの例

for(container_t<element_t>::iterator position = container.begin(); position != container.end(); ++position){
  ... --中身は*position
}
declare
   Position : Cursor := Container.First;
begin
   while Has_Element(Container) loop
      ... --中身はElement(Position)
      Next(Container);
   end loop;
end;

内部イテレータの例

Container.iter (fun item -> ...) container
declare
   procedure Process (Position : Cursor) is
   begin
      ... -- 中身はElement(Position)
   end;
begin
   Iterate(Container, Process'Access);
end;

やってられるかー!
というわけで列挙オブジェクトタイプが追加。このタイプは、文法の支援があるのも特徴ですね。

foreach (Element item in container)
{
   ...
}
for Position in Container.Iterator loop
   ... -- 中身はElement(Position)
end loop;

いきなりすっきりしました。

……いや、それはいいとして。

<ここからごみ>ここから私の主張。

C++がそうしたからでしょうけれど、外部イテレータの多くが、vectorやarray等要素がメモリ上で連続することが保証されているコンテナですら、配列のインデックスとは別物なのは、少し悲しいと思うのです。なぜvector::iteratorがssize_tではいけなかったのか?これでは、統一と称して別ライブラリ作ったら結局選択肢が増えただけでうざーなよくある現象にも思えます。
vectoriteratorは、コンテナへの参照と、インデックス値を持っていますので、レジスタにも載らなかったりして、色々と勿体ないわけですよ(と思ったがそんなヘボ実装はAda.Containers.Vectorsぐらいか?普通element_t *にするよなあ……これだからGNATは……。しかし私の使用頻度としてはAda.Containers.Vectorsのほうがstd::vectorより多いためry)。
それ以前にインデックスとイテレータの2種類があるのが嫌。
イテレータからの値の取得をoperator *ではなく、operator []で行うことで、vector::iteratorをssize_tと同一にできて、幸せになれるはずです。主にGNATで!あとポインタ演算ができない言語や範囲チェックを実行時に行いたい場合(GNATでコンテナとインデックスの組なのはどう考えてもこれが理由だろ俺)で外部イテレータを実装するとき!
C++の場合は、C言語にポインタでループしてたイディオムがありましたので、element_t *がイテレータなのは自然な流れですが、それまでインデックスアクセスしてた言語がいきなりSTL風に染まってしまうのは、なんかこう、なんかこう。お茶は続いてほしかった。なんかこう。D言語のforeachなんか、やはり不便とわかったのか後からインデックスも受け取れるようになりましたし!
逆に、リンク系コンテナで、無駄にコンテナを渡すことになりますが、使っていない引数の除去は、構造体の分離より簡単なはず……。

</ここまでごみ>

もうひとつAda.Containersには問題がありまして(もうイテレータと関係ないですが)、上でも書いている要素取り出しのためのElement関数は関数のため、型によっては毎回返値をコピーして悲惨なことになるわけです。
申し訳程度にQuery_Elementみたいなものも用意されていますが、誰が要素アクセスのために毎回こんなもの書くというのか……。

declare
   procedure Process (Item : Element_Type) is
   begin
      ... --Itemはコピーされない!
   end Process;
begin
   Query_Element (Position, Process'Access);
end;

当然のように、access型を返す関数も用意するように提案がありました。

http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai05s/ai05-0142-4.txt

function Constant_Reference (Container : aliased in Vector; Position : in Cursor)
   return Constant_Reference_Type;
function Reference (Container : aliased in out Vector; Position : in Cursor)
   return Reference_Type;
Nanka_Op (Reference(Container, Position).Element.all); -- コピーされない!

こうなるとElement関数やQuery_ElementやUpdate_Elementはお払い箱になるわけですが、Adaのことですから規格改訂時に互換性を無視してばっさりと削ぎ落としてくれることでしょう。

ここで注目して欲しいのはReference関数のシグネチャ。Element関数を定義している以上はCursorだけで要素アクセスに充分な情報があるはずなのに、Containerが引数にあります。

……いやaliased引数も大きいですよ?(でもAda.Containers.VectorsのGNAT実装の時点で、引数に'Unchecked_Accessを使ってしまっているしなあ。参照渡しとaccessが内部的に異なる環境ってあるのでしょうか)