イテレータ
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
- 外部
- 要素の位置
- 範囲
- DのRange
- C++0xでrangeコンセプトが追加されるらしい……concept廃止でどうなる?
- 列挙オブジェクト(と専用の文法)
- Pythonの__iter__
- JavaScriptの__iterator__
- .NET(含Delphi)のIEnumerable/IEnumerator
- Python、JavaScriptやC#は内部イテレータの様に定義できる
- Iconではバックトラッキングすら可能らしい
- 内部
一般には、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
vectorのiteratorは、コンテナへの参照と、インデックス値を持っていますので、レジスタにも載らなかったりして、色々と勿体ないわけですよ(と思ったがそんなヘボ実装はAda.Containers.Vectorsぐらいか?普通element_t *にするよなあ……これだからGNATは……。しかし私の使用頻度としてはAda.Containers.Vectorsのほうがstd::vectorより多いためry)。
それ以前にインデックスとイテレータの2種類があるのが嫌。
イテレータからの値の取得をoperator *ではなく、operator []で行うことで、vector
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が内部的に異なる環境ってあるのでしょうか)