Visual Studio 2008から導入された標準クエリ演算子を利用するスタイルでは、IEnumerable
を返すメソッドから始まり、中間ではIEnumerable
を受け取ってIEnumerable
を返すメソッド、最後はIEnumerable
を受け取って最終処理するメソッドを数珠(じゅず)繋ぎにします。IEnumerable
を受け取るとは引数で受け取ることを考えてしまいますが、そうではなくIEnumerable
各フィルタ内部ではyield
を使ってコンテキストスイッチを行い次のフィルタにデータを渡す処理を内部で繰り返しています。そのためフィルタを記述するのに繰り返しをその都度記述する必要はありません。
1 | using System; |
NetFrameworkではSelect
,Where
などのメソッドが用意されていますがこれらはどのように作ればよいのでしょう。IEnumerable
を利用するスタイルで使えるEach
とMap
というメソッドを作ってみます。Each
はIEnumerable
を受け取って、引数で指定したメソッドに各要素を順に渡すメソッド、Map
は、指定したメソッドに各要素を順に渡すだけでなくそのメソッドが何らかの変換を行って値を返します。IEnumerable
はNetframeworkに用意されている型ですが、作り付けの型に新たなメソッドを追加しなければならないことになります。また、IEnumerable
はクラスではなくインタフェースですから通常は実装を持たないはずです。なおさらメソッドを実装することが難しく思えます。2008からは拡張メソッドによってそれが可能になっています。
Each
,Map
は共に拡張メソッドとして実装しました。Each
の第2引数func
は各要素を受け取って処理するメソッドを指定します。Action
は引数を一つ受け取り、値を返さないメソッドを表します。一方Map
では値を返しますから、Func
となっています。Func
とは一つの引数T1
を受け取り、T2
を返すメソッドを表します。ここでは両方T
ですから引数と同じ型の値を返すのです。
使用例は、標準入力を順に読み取り、大文字にして標準出力に書き込むコンソールプログラムです。英文を入力してEnterを押せば次の行に大文字に変換された文字列が表示されます。終了させるにはcontrol-Zに続いてEnterを押します。
FileReader
は標準入力を1行ずつ読み取るメソッドです。FileReader
は次のMap
に結果を渡します。Map
の引数には大文字変換を行うラムダ式が指定されています。Map
はさらにEach
に結果を渡します。Each
の引数には渡された文字列を表示するラムダ式が指定されています。
これを発展させれば多段階の処理が必要なフィルタを実現することもできます。
この例では頻繁にyield
が呼ばれます。yield
のパフォーマンスは下記ページを参考にしてください。