Scalaのforとは

最近リファクタリングとか微妙な不具合修正やってるほさかです。 Scala の for は Java のものとはだいぶ様子が違います。そこで仕様を読みながらもう一度forについて学びます。

ここではscala-lang.orgを 読みながら自分なりの説明を書いてますので、気になる方はscala-lang.orgを読みましょう。

for とは

2種類のforがあります。 for内包表記forループ です。

for ( enum ) e

forループは 列挙enum の各要素に対して 式e を実行します。

REPLの実行例

scala> for (x <- List(1, 2, 3)) print(x)
123
for ( enum ) yield e

for内包表記は 列挙enum の各要素に対して 式e を実行し、その結果を集めて返します。

REPLの実行例

scala> for (x <- List(1, 2, 3)) yield -x
res0: List[Int] = List(-1, -2, -3)

列挙はまず ジェネレーター から始まります。その後には、さらに ジェネレーター値の定義ガード を書けます。

ジェネレーター p <- e は式eのうちパターンpにマッチするものをバインドします。 マッチしない物は無視されます。

値の定義valを省略する以外はいつもどおりです。

ガード if e は列挙のバインドを e が真のものに限定します。

これらはそれぞれ map flatMap withFilter foreach で実装されています。 これらのメソッドを実装したものであればジェネレーターにできます。

変換ルール

for内包表記 for (p <- e) yield e′e.map { case p => e′ } に変換されます。 forループ for (p <- e) e′e.foreach { case p => e′ } に変換されます。

for内包表記 for (p <- e; p′ <- e′;…) yield e″ (は空かジェネレーターか値の定義かガード) は e.flatMap { case p => for (p′ <- e′;…) yield e″ } に変換されます。

forループ for (p <- e; p′ <- e′;…) e″ (は空かジェネレーターか値の定義かガード) は e.foreach { case p => for (p′ <- e′;…) e″ } に変換されます。

ガード if g をともなう ジェネレーター p <- e は 一つのジェネレーター p <- e.withFilter((x1,…,xn) => g) (x1,…,xn は p の変数) に変換されます。

値の定義 p′ = e′ をともなう ジェネレーター p <- e は 値のペアを持つジェネレーターをともなって変換されます。 (p, p′) <- for (x@p <- e) yield { val x′@p′ = e′; (x, x′) }

おわり

forやScalaに限らず疑問があれば仕様を読めば良いと思います。