Iterators Must Go                            Andrei Alexandrescu©2009 Andrei Alexandrescu
This Talk •   The STL •   Iterators •   Range-based design •   Conclusions©2009 Andrei Alexandrescu
STLとは何か?©2009 Andrei Alexandrescu
STLとは何か? • アルゴリズムとデータ構造の(良い|悪い|醜い)ライブラリ • iterators = gcd(containers, algorithms); • Scrumptious Template Lore(すばらしいテンプレート...
STLとは何かというと • STLでは答えよりも質問の方が重要 • “基本的なコンテナとアルゴリズムの中で最も一般的な   実装はどのようなものでしょうか?” • ほかのものは全てその余波 • STLで最も重要なこと:       STLはひと...
STLは非直観的 • 相対性理論が非直観的なのと同じ方向 • 複素数が非直観的なのと同じ方向                            n√-1形式の値は”虚数”だが                            方程式で使...
非直観的 • “私は最も一般的なアルゴリズムを設計したい。” • “もちろんできる。あなたが確実に必要とするものは、イテレータと呼ばれ   るものである。正確に言うとそれらのうちの5つ。” • 証拠:どんな言語も”たまたま”STLをサポートしな...
Fundamental vs Incidental in STL (基礎 vs 偶発的なSTL) •    F:アルゴリズムは可能な限り狭いインタフェースで定義される •    F:アルゴリズムに必要とされる広いイテレータカテゴリ •    I...
STL:良い点 • 正しい質問ができる • 一般的 • 効率的 • 無理なく拡張可能 • 組み込み型に統合された©2009 Andrei Alexandrescu
STL:悪い点 • ラムダ関数のサポートが貧弱      • STLの問題ではない      • 高い機会費用 • いくつかのコンテナはサポートできない      – たとえば、sentinel-terminatedコンテナ      – たと...
STL:醜い点 • for_each等の試みは助けにならなかった • ストリームによる統合が希薄 • 1単語:allocator • イテレータの最低なところ      – 冗長      – 安全ではない      – 貧弱なインタフェース©...
イテレータの取り決めは何か?©2009 Andrei Alexandrescu
Iterators Rock • コンテナとアルゴリズム間の相互作用をまとめ上げる • “強度低下:”m・nの代わりにm+nを実装 • 拡張可能:ここで、STLが日の目を見て以来、イテレータに動   揺が走った©2009 Andrei Alex...
危険信号 #1 • 2001年頃にC++ Users Journalは広告キャンペーンを行った      – 「CUJに記事を投稿してください!」      – 「英文学を専攻している必要はありません!        エディタで始めてください!...
危険信号 #2 • およそ1975年のファイルコピー #include <stdio.h> int main() {     int c;     while ((c = getchar()) != EOF)         putchar(c...
危険信号 #2 • 20年ほど早送りして… #include <iostream> #include <algorithm> #include <iterator> #include <string> using namespace std; ...
(mainの中にtry/catchを書くのを忘れた)©2009 Andrei Alexandrescu
(何かどこか、逆にひどくなってしまった)©2009 Andrei Alexandrescu
危険信号 #3 • イテレータは定義するのが非常に難しい • かさばった実装と多くの了解事項(gotcha) • Boostはイテレータの定義を支援する完全なライブラリを含ん   でいる • 本質的なプリミティブは…3つ?      – 終了(...
危険信号 #4 • イテレータはポインタの構文とセマンティクスを使用する • 勝利/敗北のポインタとの統合 • しかし、これはイテレーションの方法を制限する      – パラメータを持った++が必要なので、深いツリーを歩かせることがで    ...
最終的な命取り • 全てのイテレータプリミティブは基本的に安全ではない • ほとんどのイテレータは、与えられたイテレータのために      – 比較できるかどうかを記述することができない      – インクリメントできるかどうかを記述すること...
Range©2009 Andrei Alexandrescu
Enter Range • これらの不便さを部分的に回避するため、   Rangeが定義された • Rangeは、begin/endイテレータの組をパックしたもの • そのため、Rangeはより高レベルのcheckable invariant(...
それらはよい方向におもしろい一歩を踏み出した©2009 Andrei Alexandrescu
より一層理解されなけばならない©2009 Andrei Alexandrescu
ほら、どこにもイテレータがない! • イテレータの代わりにRangeをイテレーション用の   基本構造として定義してはどうだろう? • Rangeは、イテレーションに依存しない基本処理を定義するべき • 今後イテレータはなく、Rangeだけが残...
Rangeの定義 • <algorithm>は全てRangeで定義でき、他のアルゴリズムも同様にRangeで   実装できる • Rangeプリミティブは、少ないコストでチェック可能でなければならない • イテレータより非効率であってはならない...
Input/Forward Range       template<class T> struct InputRange {           bool empty() const;           void popFront();  ...
証明可能? template<class T> struct ContigRange {     bool empty() const { return b >= e; }     void popFront() {         asser...
検索 // オリジナル版のSTL template<class It, class T> It find(It b, It e, T value) {     for (; b != e; ++b)     if (value == *b) b...
設計質問 • Rangeでのfindはどのように見えなければならないか?      1. 範囲のひとつの要素(見つかった場合)、         もしくは0個の要素(見つからなかった場合)を返す?      2. 見つかった要素以前のRange...
検索 // Rangeを使用 template<class R, class T> R find(R r, T value) {     for (; !r.empty(); r.popFront())         if (value ==...
エレガントな仕様     template<class R, class T>     R find(R r, T value);      「frontがvalueと等しいか、rが使い尽くされるまで、        左から範囲rを縮小する」©...
Bidirectional Range       template<class T> struct BidirRange {           bool empty() const;           void popFront();  ...
Reverse Iteration template<class R> struct Retro {     bool empty() const { return r.empty(); }     void popFront() { retu...
find_endはどうだろう?     template<class R, class T>     R find_end(R r, T value) {         return retro(find(retro(r));     } •...
イテレータでのfind_endは最低だ     // reverse_iteratorを使用したfind_end     template<class It, class T>     It find_end(It b, It e, T val...
さらなる構成の可能性 • Chain : いくつかのRangeをつなげる         要素はコピーされない!         Rangeのカテゴリは、全てのRangeの中で最も弱い • Zip : 密集行進法(lockstep)でRange...
3つのイテレータの関数はどうだろうか? template<class It1, class It2> void copy(It1 begin, It1 end, It2 to); template<class It> void partial_...
「困難なところにはチャンスがある。」         "Where theres hardship, theres opportunity."                                                   ...
3-legged algos ⇒ mixed-range algos     template<class R1, class R2>     R2 copy(R1 r1, R2 r2); • 仕様:r1からr2へコピーして、手をつけていないr...
3-legged algos ⇒ mixed-range algos     template<class R1, class R2>     void partial_sort(R1 r1, R2 r2); • 仕様:最も小さな要素がr1の中...
ちょっと待って、まだある     vector<double> v1, v2;     deque<double> d;     partial_sort(v1, chain(v2, d));     sort(chain(v1, v2, d)...
ちょっと待って、さらにある     vector<double> vd;     vector<string> vs;     // 密集行進法(lockstep)で2つをソートする     sort(zip(vs, vd)); • Range...
Output Range  •   ポインタ構文からの解放されたので、異なる型をサポートすることが可能になった      struct OutRange {          typedef Typelist<int, double, stri...
stdinからstdoutにコピーする話に戻ろう       #include <...>       int main() {           copy(istream_range<string>(cin),               ...
Infinite Range(無限の範囲) • 無限についての概念はRangeでおもしろくなる • ジェネレータ、乱数、シリーズ、… はInfinite Range • 無限は、5つの古典的カテゴリとは異なる特性;   あらゆる種類のRange...
has_size • Rangeが効率的に計算されたsizeを持っているかどうかは他の独立した特   性 • 索引項目:list.size, 永遠の討論 • Input Rangeさえ、既知のサイズを持つことができる   (たとえば、100個の...
予想外の展開(A Twist) • Rangeで<algorithm>をやり直すことができる? • Dのstdlibは、std.algorithmとstd.rangeモジュールで<algorithm>のスー   パーセットを提供している(goo...
結論 • Rangeは優れた抽象的概念である • より良いチェック能力(まだ完全ではない) • 容易な構成 • Rangeに基づいた設計は、イテレータに基づいた関数をはるかに超えるも   のを提供する • 一歩進んだSTLによる刺激的な開発©2...
Upcoming SlideShare
Loading in...5
×

Iterators must-go(ja)

1,596

Published on

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,596
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
4
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Iterators must-go(ja)

  1. 1. Iterators Must Go Andrei Alexandrescu©2009 Andrei Alexandrescu
  2. 2. This Talk • The STL • Iterators • Range-based design • Conclusions©2009 Andrei Alexandrescu
  3. 3. STLとは何か?©2009 Andrei Alexandrescu
  4. 4. STLとは何か? • アルゴリズムとデータ構造の(良い|悪い|醜い)ライブラリ • iterators = gcd(containers, algorithms); • Scrumptious Template Lore(すばらしいテンプレートの知恵) • Snout to Tail Length(先頭から最後尾までの長さ)©2009 Andrei Alexandrescu
  5. 5. STLとは何かというと • STLでは答えよりも質問の方が重要 • “基本的なコンテナとアルゴリズムの中で最も一般的な 実装はどのようなものでしょうか?” • ほかのものは全てその余波 • STLで最も重要なこと: STLはひとつの答えではあるが、答えではない©2009 Andrei Alexandrescu
  6. 6. STLは非直観的 • 相対性理論が非直観的なのと同じ方向 • 複素数が非直観的なのと同じ方向 n√-1形式の値は”虚数”だが 方程式で使用することができる。©2009 Andrei Alexandrescu
  7. 7. 非直観的 • “私は最も一般的なアルゴリズムを設計したい。” • “もちろんできる。あなたが確実に必要とするものは、イテレータと呼ばれ るものである。正確に言うとそれらのうちの5つ。” • 証拠:どんな言語も”たまたま”STLをサポートしなかった。 • 無慈悲な”機能戦争”にもかかわらず • C++とDが唯一 • どちらもSTLのサポートを積極的に目指した • 結果:STLは、C++とDの外から理解するのが非常に困難になった。©2009 Andrei Alexandrescu
  8. 8. Fundamental vs Incidental in STL (基礎 vs 偶発的なSTL) • F:アルゴリズムは可能な限り狭いインタフェースで定義される • F:アルゴリズムに必要とされる広いイテレータカテゴリ • I:iterator primitiveの選択 • I:iterator primitiveの構文©2009 Andrei Alexandrescu
  9. 9. STL:良い点 • 正しい質問ができる • 一般的 • 効率的 • 無理なく拡張可能 • 組み込み型に統合された©2009 Andrei Alexandrescu
  10. 10. STL:悪い点 • ラムダ関数のサポートが貧弱 • STLの問題ではない • 高い機会費用 • いくつかのコンテナはサポートできない – たとえば、sentinel-terminatedコンテナ – たとえば、分散ストレージを持つコンテナ • いくつかの反復法はサポートできない©2009 Andrei Alexandrescu
  11. 11. STL:醜い点 • for_each等の試みは助けにならなかった • ストリームによる統合が希薄 • 1単語:allocator • イテレータの最低なところ – 冗長 – 安全ではない – 貧弱なインタフェース©2009 Andrei Alexandrescu
  12. 12. イテレータの取り決めは何か?©2009 Andrei Alexandrescu
  13. 13. Iterators Rock • コンテナとアルゴリズム間の相互作用をまとめ上げる • “強度低下:”m・nの代わりにm+nを実装 • 拡張可能:ここで、STLが日の目を見て以来、イテレータに動 揺が走った©2009 Andrei Alexandrescu
  14. 14. 危険信号 #1 • 2001年頃にC++ Users Journalは広告キャンペーンを行った – 「CUJに記事を投稿してください!」 – 「英文学を専攻している必要はありません! エディタで始めてください!」 – 「私たちはセキュリティ、ネットワーク技術、C++技術、その他の技術に 興味があります!」 注:まだ他のイテレータには興味がありませんでした • 公開されたイテレータはどれくらい生き残った?©2009 Andrei Alexandrescu
  15. 15. 危険信号 #2 • およそ1975年のファイルコピー #include <stdio.h> int main() { int c; while ((c = getchar()) != EOF) putchar(c); return errno != 0; }©2009 Andrei Alexandrescu
  16. 16. 危険信号 #2 • 20年ほど早送りして… #include <iostream> #include <algorithm> #include <iterator> #include <string> using namespace std; int main() { copy(istream_iterator<string>(cin), istream_iterator<string>(), ostream_iterator<string>(cout,"¥n")); }©2009 Andrei Alexandrescu
  17. 17. (mainの中にtry/catchを書くのを忘れた)©2009 Andrei Alexandrescu
  18. 18. (何かどこか、逆にひどくなってしまった)©2009 Andrei Alexandrescu
  19. 19. 危険信号 #3 • イテレータは定義するのが非常に難しい • かさばった実装と多くの了解事項(gotcha) • Boostはイテレータの定義を支援する完全なライブラリを含ん でいる • 本質的なプリミティブは…3つ? – 終了(At end) – アクセス(Access) – 衝突(Bump)©2009 Andrei Alexandrescu
  20. 20. 危険信号 #4 • イテレータはポインタの構文とセマンティクスを使用する • 勝利/敗北のポインタとの統合 • しかし、これはイテレーションの方法を制限する – パラメータを持った++が必要なので、深いツリーを歩かせることがで きない – OutputIteratorはひとつの型しか受け取ることができない: それらは全て同じ場所へ行くが、output_iteratorは出力するそれぞれ の型でパラメータ化されなければならない©2009 Andrei Alexandrescu
  21. 21. 最終的な命取り • 全てのイテレータプリミティブは基本的に安全ではない • ほとんどのイテレータは、与えられたイテレータのために – 比較できるかどうかを記述することができない – インクリメントできるかどうかを記述することができない – 間接参照できるかどうかを記述することができない • 安全なイテレータを書くことができる – 高いサイズ+速度のコストで – たいていは、設計をカットできなかったというよい議論 (訳注:かなり怪しい訳)©2009 Andrei Alexandrescu
  22. 22. Range©2009 Andrei Alexandrescu
  23. 23. Enter Range • これらの不便さを部分的に回避するため、 Rangeが定義された • Rangeは、begin/endイテレータの組をパックしたもの • そのため、Rangeはより高レベルのcheckable invariant(訳注: チェック可能な不変式?)を持つ • BoostとAdobeのライブラリはRangeを定義した©2009 Andrei Alexandrescu
  24. 24. それらはよい方向におもしろい一歩を踏み出した©2009 Andrei Alexandrescu
  25. 25. より一層理解されなけばならない©2009 Andrei Alexandrescu
  26. 26. ほら、どこにもイテレータがない! • イテレータの代わりにRangeをイテレーション用の 基本構造として定義してはどうだろう? • Rangeは、イテレーションに依存しない基本処理を定義するべき • 今後イテレータはなく、Rangeだけが残るだろう(訳注:怪しい) • Rangeはどのプリミティブをサポートしなければならないか begin/endがオプションではないことを思い出してほしい 人々が個々のイテレータを貯め込んでいたら、我々は振り出しに戻らなければならない©2009 Andrei Alexandrescu
  27. 27. Rangeの定義 • <algorithm>は全てRangeで定義でき、他のアルゴリズムも同様にRangeで 実装できる • Rangeプリミティブは、少ないコストでチェック可能でなければならない • イテレータより非効率であってはならない©2009 Andrei Alexandrescu
  28. 28. Input/Forward Range template<class T> struct InputRange { bool empty() const; void popFront(); T& front() const; };©2009 Andrei Alexandrescu
  29. 29. 証明可能? template<class T> struct ContigRange { bool empty() const { return b >= e; } void popFront() { assert(!empty()); ++b; } T& front() const { assert(!empty()); return *b; } private: T *b, *e; };©2009 Andrei Alexandrescu
  30. 30. 検索 // オリジナル版のSTL template<class It, class T> It find(It b, It e, T value) { for (; b != e; ++b) if (value == *b) break; return b; } ... auto i = find(v.begin(), v.end(), value); if (i != v.end()) ...©2009 Andrei Alexandrescu
  31. 31. 設計質問 • Rangeでのfindはどのように見えなければならないか? 1. 範囲のひとつの要素(見つかった場合)、 もしくは0個の要素(見つからなかった場合)を返す? 2. 見つかった要素以前のRangeを返す? 3. 見つかった要素以降のRangeを返す? • 正解:(もしあった場合)見つかった要素で始まるRangeを返し、そうでなければ空 を返す なぜ?©2009 Andrei Alexandrescu
  32. 32. 検索 // Rangeを使用 template<class R, class T> R find(R r, T value) { for (; !r.empty(); r.popFront()) if (value == r.front()) break; return r; } ... auto r = find(v.all(), value); if (!r.empty()) ...©2009 Andrei Alexandrescu
  33. 33. エレガントな仕様 template<class R, class T> R find(R r, T value); 「frontがvalueと等しいか、rが使い尽くされるまで、 左から範囲rを縮小する」©2009 Andrei Alexandrescu
  34. 34. Bidirectional Range template<class T> struct BidirRange { bool empty() const; void popFront(); void popBack(); T& front() const; T& back() const; };©2009 Andrei Alexandrescu
  35. 35. Reverse Iteration template<class R> struct Retro { bool empty() const { return r.empty(); } void popFront() { return r.popBack(); } void popBack() { return r.popFront(); } E<R>::Type& front() const { return r.back(); } E<R>::Type& back() const { return r.front(); } R r; }; template<class R> Retro<R> retro(R r) { return Retro<R>(r); } template<class R> R retro(Retro<R> r) { return r.r; // klever(訳注:clever? : 賢いところ) }©2009 Andrei Alexandrescu
  36. 36. find_endはどうだろう? template<class R, class T> R find_end(R r, T value) { return retro(find(retro(r)); } • rbegin, rendは必要ない • コンテナは、範囲を返すallを定義する • 後ろにイテレートする:retro(cont.all())©2009 Andrei Alexandrescu
  37. 37. イテレータでのfind_endは最低だ // reverse_iteratorを使用したfind_end template<class It, class T> It find_end(It b, It e, T value) { It r = find(reverse_iterator<It>(e), reverse_iterator<It>(b), value).base(); return r == b ? e : --r; } • Rangeが圧倒的に有利:はるかに簡潔なコード • 2つを同時に扱うのではなく、1つのオブジェクトのみから構 成されるので、容易な構成になる©2009 Andrei Alexandrescu
  38. 38. さらなる構成の可能性 • Chain : いくつかのRangeをつなげる 要素はコピーされない! Rangeのカテゴリは、全てのRangeの中で最も弱い • Zip : 密集行進法(lockstep)でRangeを渡る Tupleが必要 • Stride : 一度に数ステップ、Rangeを渡る イテレータではこれを実装することができない! • Radial : 中間(あるいはその他のポイント)からの距離を増加させる際に Rangeを渡る©2009 Andrei Alexandrescu
  39. 39. 3つのイテレータの関数はどうだろうか? template<class It1, class It2> void copy(It1 begin, It1 end, It2 to); template<class It> void partial_sort(It begin, It mid, It end); template<class It> void rotate(It begin, It mid, It end); template<class It, class Pr> It partition(It begin, It end, Pr pred); template<class It, class Pr> It inplace_merge(It begin, It mid, It end);©2009 Andrei Alexandrescu
  40. 40. 「困難なところにはチャンスがある。」 "Where theres hardship, theres opportunity." – I. Meade Etop©2009 Andrei Alexandrescu
  41. 41. 3-legged algos ⇒ mixed-range algos template<class R1, class R2> R2 copy(R1 r1, R2 r2); • 仕様:r1からr2へコピーして、手をつけていないr2を返す vector<float> v; list<int> s; deque<double> d; copy(chain(v, s), d);©2009 Andrei Alexandrescu
  42. 42. 3-legged algos ⇒ mixed-range algos template<class R1, class R2> void partial_sort(R1 r1, R2 r2); • 仕様:最も小さな要素がr1の中で終了するように、r1とr2の連結を 部分的にソートする • あなたは、vectorとdequeをとり、両方の中で最も小さな要素を配列に入 れることができる vector<float> v; deque<double> d; partial_sort(v, d);©2009 Andrei Alexandrescu
  43. 43. ちょっと待って、まだある vector<double> v1, v2; deque<double> d; partial_sort(v1, chain(v2, d)); sort(chain(v1, v2, d)); • アルゴリズムは今、余分な労力なしで任意のRangeの組み合わせにおい ても途切れることなく動作することができる • イテレータでこれを試してみて!©2009 Andrei Alexandrescu
  44. 44. ちょっと待って、さらにある vector<double> vd; vector<string> vs; // 密集行進法(lockstep)で2つをソートする sort(zip(vs, vd)); • Rangeコンビネータは、無数の新しい使い方ができるようになる • イテレータでも理論上は可能だが、(再び)構文が爆発する©2009 Andrei Alexandrescu
  45. 45. Output Range • ポインタ構文からの解放されたので、異なる型をサポートすることが可能になった struct OutRange { typedef Typelist<int, double, string> Types; void put(int); void put(double); void put(string); };©2009 Andrei Alexandrescu
  46. 46. stdinからstdoutにコピーする話に戻ろう #include <...> int main() { copy(istream_range<string>(cin), ostream_range(cout, "¥n")); } 1 • 最後にもう一歩:1行(one-line)に収まる寸言 (one-liner) • ostream_rangeにはstringを指定する必要はない 1 スライド制限にもかかわらず©2009 Andrei Alexandrescu
  47. 47. Infinite Range(無限の範囲) • 無限についての概念はRangeでおもしろくなる • ジェネレータ、乱数、シリーズ、… はInfinite Range • 無限は、5つの古典的カテゴリとは異なる特性; あらゆる種類のRangeが無限かもしれない • ランダムアクセスなRangeさえ無限かもしれない! • 無限について静的に知っておくことは、アルゴリズムを助ける©2009 Andrei Alexandrescu
  48. 48. has_size • Rangeが効率的に計算されたsizeを持っているかどうかは他の独立した特 性 • 索引項目:list.size, 永遠の討論 • Input Rangeさえ、既知のサイズを持つことができる (たとえば、100個の乱数をとるtake(100, rndgen)) – rが無限の場合、take(100, r)は100の長さ – rが長さを知っている場合、長さはmin(100, r.size()) – rが未知の長さで有限の場合、未知の長さ©2009 Andrei Alexandrescu
  49. 49. 予想外の展開(A Twist) • Rangeで<algorithm>をやり直すことができる? • Dのstdlibは、std.algorithmとstd.rangeモジュールで<algorithm>のスー パーセットを提供している(googleで検索してほしい) • RangeはD全体に渡って利用されている: アルゴリズム、遅延評価、乱数、高階関数、foreach文… • いくつかの可能性はまだ簡単には挙げられなかった – たとえばfilter(input/output range) • Doctor Dobb‘s Journalの「The Case for D」をチェックしておいてください。 coming soon...©2009 Andrei Alexandrescu
  50. 50. 結論 • Rangeは優れた抽象的概念である • より良いチェック能力(まだ完全ではない) • 容易な構成 • Rangeに基づいた設計は、イテレータに基づいた関数をはるかに超えるも のを提供する • 一歩進んだSTLによる刺激的な開発©2009 Andrei Alexandrescu
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×