kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

Scalaの標準(コレクション)APIを読もう:車輪を再発明する前に

なんか久しぶりに、技術ネタっぽい何かです。

Scalaの標準ライブラリは結構偏りがあって、scala.ioとかのIO周りは壊滅的だったりする(ので早くScala-IO入って欲しい)のですが、コレクションライブラリは結構充実してて、色々便利メソッドがあったりします。コレクション絡みで何か非常に短めのメソッド再発名発明したくなったら、APIリファレンスを読んでみると良いかもしれません。

Optionは、Scalaでは非常に色々なところで使われており、それゆえにScalaでnullが明示的に使われる場面はあまりありません。しかし、Optionは、Some(x)かNoneの2種類しかなく、Noneの場合にどのようなエラーが発生したかという情報を持ち運ぶことができません。そのような場合、Eitherを使うことができますが、OptionからEitherに変換したい場合があります。そのような場合、Option#tRight(left)を使えます。これは、OptionがSome(x)の場合はRight(x)に、Noneの場合、Left(left)に変換してくれるメソッドです。積極的に使いましょう。

これはScalaistには割と常識かもしれませんが、一応。ScalaのOptionは、パターンマッチを使って

val result = option match {
  case Some(x) => x
  case None => y
}

のように書くことができ、どちらかが欠けているとコンパイラが警告を出してくれます。しかし、Optionに対してパターンマッチばっかり使っているとタイプ数とか多くなってウザい事がありあす。そういうときは、Option#getOrElse
を使って、

val result = option.getOrElse(y)

のように書きましょう。

ひょっとしたら空かもしれないコレクションから最初の要素を取り出したい場合、headOptionを使いましょう。headOptionは最初の要素があった場合、Some(elem)、無かった場合Noneを返してくれます。コレクションが空である事が正常系としてありえる場合にheadをそのまま使うと例外がthrowされて、try-catch使うはめになってウザい(それ以前に正常系で空である可能性があるのなら、headを使うべきではないですが)のでやめましょう。

  • 高階関数を使わなくて同じ目的を達成できるならそのメソッドを使おう

関数型プログラミングでは、ループより再帰、再帰より高階関数が推奨されますが、そもそ高階関数すら必要無い場合があります。たとえば、Traversable#sumは、Numericに適合する型ならなんでも合計値を計算することができます。foldLeftを使って、

list.foldLeft(z)(_+_)

のように書くこともできますが、要素がNumericに適合するなら 単純に

list.sum

と書くべきです。かくいう自分もコレクションライブラリにあるメソッドを再発明してしまったりしたことがあるのですが、少なくとも標準コレクションライブラリだけでもそれなりに色々メソッドがあるので、まずはコレクションライブラリのAPIリファレンスを読んでみましょう。Scalazとか言い出すのは(Scalazは標準コレクションライブラリに無い色々なものを補完してくれます)その後でも遅くありません。

なお、本文で書いてあるTraversable#sumとかは正確にはTraversableLike#sumとかだったりするのですが、その辺りを語りだすとめんどいので省きました。