このセッションでは、イテレータと コレクションについてお話しします。
Pharo でイテレータによって コレクションの中を列挙する方法を見てみましょう。
Pharo のイテレータの力がわかるでしょう。
よく使う主なイテレータを見てみましょう。
最初の例です。
これは Java で書いた
コレクションを列挙するコードです。
コレクション persons を列挙して
名簿を抽出します。
Pharo ではこのコードを書きます。
collect: イテレータを使います。 これについては後でお話しします。
人々の全ての名前を集めます。
ところで、Java の最新版の Java 8 では
レキシカルクロージャを導入しました。
ブロックと等価なものです。
その結果、構文が Pharo に近付きました。
しかし、Pharo では 最初からブロックがありました。
ブロックはこの言語の心臓部です。
プログラマにとても大きな表現力を 与えます。
とても多くのイテレータがあります。
まず collect: があります。コレクションに collect: を送ることで何ができるでしょうか？
これは正や負の数のコレクションです。
collect: メッセージを送って ブロックを渡します。
コレクションを列挙する時は
順番にブロックの引数になります。 2, -3, 4, …という具合に。
そして abs メッセージを送って 絶対値を求めます。
abs メッセージはこの数の絶対値が 欲しいという意味です。
コレクションのそれぞれの要素に対して ブロックを適用したら
結果が新しいコレクションに集約されます。
collect: に返される結果は 新しいコレクションです。
ブロックがコレクションの各要素に 適用されています。
2 の絶対値 -3 の絶対値つまり 3
4 の絶対値の 4 -35 の絶対値の 35
そして 4 の絶対値の 4
覚えておいておくべきことは
オブジェクトで考えるということです。
コレクションに何かをするようにお願いします。
コレクションは自分で その要素を列挙します。
各要素への処理を与えます。
これがイテレータの大事な部分です。
これは collect: の別の例です。
このコレクションに collect: メッセージを送ります。
ブロックでは毎回
要素が奇数か尋ねます。
その結果を全て集約します。
false、true、false、true、と。
もちろん
他の言語でやるように
ブロックやイテレータを使わずに 書くこともできます。
コレクションがあり
結果のコレクションを作り
1 から コレクションの大きさまで 整数を列挙します。
to:do: を使います。
コレクションを列挙して result コレクションに追加していきます。
こうやって書くこともできます。 全く同じことです。
あなた次第です。 シンプルに書きたいのか
それとも、ややこしく書きたいか。 それだけの話です。
私はシンプルなやり方のほうが断然良いです。
Pharo のコレクションの階層では
全てのコレクションは多相的で
Collectionクラスを継承しています。 つまり、共通の API があります。
長所として、大部分のコレクションで
同じイテレータを使えます。
それがオブジェクトで考えるということです。
コレクションに 自分の中身を列挙するように言います。
言った側は Dictionary（辞書）を 使っているのかどうかも、わかりません。
我々はキーや値がどうしたという 内部的なロジックを知りたくはありません。
だからコレクションに行儀よくお願いして
その要素を処理してもらうのです。
沢山のイテレータが使えます。 do: と collect: はもう見ましたね。
他にももっとあります。 select:、 reject:、 detect:、などなど。
このコースではそのうちいくつかを 後で学びます。
do: は最も単純なイテレータです。
do: はコレクションの各要素を列挙します。 トランスクリプトに表示します。
もう何度も勉強しましたね。
新しいイテレータがあります。select: です。
コレクションの要素のうち
条件に合ったもの全てを得ます。
コレクションの要素のうち 奇数のものを得ます。
このコレクションに select: を送ります。
ブロックを渡します。
ブロックの値が true であれば
その要素は結果のコレクションに追加されます。
select: #odd と全く同じです。
ブロックの中で
ブロックの引数に単項メッセージを
送っているだけであれば
メッセージの名前をシンボルとして 渡すことができます。
このほうが、より短く書けます。
単項メッセージしか使えません。
他のイテレータとしては reject: があります。
このコレクションの要素のうち 奇数のものを取り除いたものを得ます。
結果として偶数のみが残っています。
また、detect: もあります。
条件に合う最初の要素を見つけます。
つまりブロックの値が true になるものです。
このコレクションの最初の奇数要素を得ます。 11 です。
条件に合う最初の要素が欲しいわけですが
条件に合うものが 1 つもない場合には デフォルトが欲しくなります。
detect:ifNone: です。
もし条件に合うものがなければ このブロックの値を返します。
この場合、0 です。
他にもプログラマを楽にしてくれる イテレータがあります。
例えば、anySatisfy: です。
条件に合う要素が 1 つでもあるか確認します。
全ての要素が条件に合うか確認する ことができます。
末尾から逆順にコレクションを列挙する ことができます。
コレクションのインデックスと要素を列挙したり 要素を2つずつ列挙することもできます。
要素から全ての順列を列挙することができます。
沢山のイテレータがあります。
新しいイテレータを作ることもできます。
コレクション 1 2 3 を
もう 1 つのコレクションと一緒に 列挙します。
do: の引数のブロックには :x と :y の 2 つの引数があります。
:x は1つ目のコレクションの要素
:y は2つ目のコレクションの要素です。
これらの要素を掛け算します。
結果は 10、40、90 です。
もちろん、このイテレータを使うには コレクションの長さが同じでなければなりません。
他にもあります。
ここでは do:separatedBy: を使っています。
コレクションの各要素を列挙しますが
1つの要素を挙げる毎に
コンマを書き出すブロックを評価します。
つまり、a を挙げて
コンマを表示して、次に b で 次にコンマ、次に c となります。
各要素の間に 1つのアクションを実行します。
これは groupBy: イテレータです。
コレクションの要素を条件に従って
グループ分けします。
コレクション 1 2 3 4 5 6 7 に このメッセージを送ります。
結果として辞書が得られます。
この #even という条件に対して
false だった全ての要素がこれです。
見ての通り 奇数の要素全てです。
偶数の要素はすべて true を返します。
よくあることですが
コレクションの中にコレクションを 入れ子にしがちです。
結果として深いレベルの入れ子になります。
これは手で作った例ですが
コレクションの中にコレクションが 入れ子になっています。
このコレクションをフラットにします。 それぞれの要素を引っ張り出して。
Pharo では簡単な方法があります。
flatCollect: イテレータです。
要素を列挙して 新しい平坦なコレクションを作ります。
コレクション 1 2 3 4 5 6 が得られます。
入れ子は無くなっています。
Pharo の全てのイテレータについて 説明するつもりはありません。
そんなことしたらダラダラと退屈なことになります。 ただ、沢山あるということはお見せしたいと思います。
コレクションのクラスを読んで
自分でイテレータを定義することができます。
それを見てみましょう。
とりあえず 既に知っているイテレータから出発します。
do: はどうやって実装されているでしょうか？
コレクションの階層で見てみましょう。
SequenceableCollection に実装されています。
do: メソッドは aBock を引数に取ります。
Collectionで定義されているものを実装しています。
1 to: self size do: [i:| aBlock
引数として渡されたブロックに
i 番目の要素を渡して評価します。
とても簡単です。
見てきた通り、Pharo では イテレータはとても強力です。
各コレクションがイテレータを 多相的にサポートしています。
プログラマは
コレクションクラスで実装された イテレータを使うことで
そのコレクションに従って 列挙することができます。
新しいイテレータを定義することができます。 とても面白いです。
コレクションクラスに自分のイテレータを 定義することができます。
デザインパターンでのイテレータパターンを 知っている人には気になるかもしれませんが
開発者はいつ次の要素を取り出すかを 決めることはできません。
コレクションが内部でそれを決めます。
next を直接イテレータに 送るのではありません。
イテレータパターンを知っている人には 違和感があるかもしれません。
イテレータはとても強力です。
プログラマの最高の友達です。
プログラムを書くことを簡単にします。
コードを短く、簡潔に、エレガントに 書くことができます。
コレクションの
データのカプセル化を確保します。