Iterator.continuallyでお手軽入力処理

皆様、Scala 2.8.0で追加された、Iterator.continually()というメソッドはご存知でしょうか。これ、実はあまり知られて無いような気がするのですが、非常に

便利な機能です。特に難しいことも無いので、コードで実例を見ればすぐに便利さがわかるかと思います。

val lines = Iterator.continually(readLine()).takeWhile(_ != null)

lines.filterNot(_.matches("""\s*""")).foreach(println)

これは、標準入力から一行ずつ読み取って、空行を取り除いて出力する、というプログラムです。ポイントは、

Iterator.continually(readLine())

の部分で、これだけで、標準入力から各行を読むIteratorが出来ます。Iterator.continuallyは名前渡し引数なので、特に無名関数を作って渡す必要が無く、入力を読み取る式をそのまま書くだけで

大丈夫です。readLine()はファイルの末尾に到達したときにnullを返すので、

val lines = Iterator.continually(readLine()).takeWhile(_ != null)

のようにして、入力が無限に続かないようにすれば、あとはfilter()したりmap()したりと好きなように加工できます。

なお、当たり前の話ですが、Iteratorは一度たどってしまうと戻れないので、入力を一通り取っておきたい場合は、Iterator#toList()などを使って先に不変コレクションに変換しておくと都合が良いです。

現状、Scalaの標準IOライブラリはscala.io.Sourceのみで、使い勝手もあまりよくありませんが、Iterator.continually()を活用することで、入力処理に関してはかなり便利になります。たとえば、

今回は文字入力だけを扱いましたが、バイトストリームやオブジェクトストリームですら同じようにして扱うことができます。

というわけで、皆さん、Iterator.continually()を活用して、快適なScalaライフを楽しんでください。

ちなみに、現在、scala-ioという、Scala用のIOライブラリが、Scala Incubatorのサブプロジェクトとして開発されています。scala-ioは、現在のscala.ioとは違い、入出力を一通り「Scalaらしく」扱うことができますし、コマンドラインパーザなども入っています。これがいつのバージョンで入るかは確定していないのですが、Odersky教授の発表資料などから、将来的に入るのはほぼ確実と言ってよいかと思います。scala-ioは、現状で、既にそれなりに使えるレベルに達していますので、標準に入るのを待てない方は、一度試してみるのも良いかもしれません。