Tour of Scala

for内包表記

Language

Scalaはシーケンス内包表記を表現するための軽量な記法を提供します。 内含表記はfor (enumerators) yield eという形をとります。enumeratorsはセミコロンで区切られたEnumeratorのリストを指します。 1つのenumeratorは新しい変数を導き出すジェネレータかフィルタのどちらかです。 内包表記はenumeratorsが生成する束縛一つ一つについて本体eを評価し、これらの値のシーケンスを返します。

こちらは例です。

case class User(name: String, age: Int)

val userBase = List(User("Travis", 28),
  User("Kelly", 33),
  User("Jennifer", 44),
  User("Dennis", 23))

val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30))
  yield user.name  // これをリストに追加する

twentySomethings.foreach(name => println(name))  // prints Travis Dennis

このyield文と一緒に使われているforループは実際にはListを生成します。 yield user.nameを返しているので、型はList[String]になります。 user <- userBaseはジェネレータであり、if (user.age >=20 && user.age < 30)は20代ではないユーザーをフィルターするガードです。

こちらは2つのジェネレータを使ったより複雑な例です。 合計が与えられた値vと等しくなる、0からn-1の全てのペアを計算します。

def foo(n: Int, v: Int) =
   for (i <- 0 until n;
        j <- 0 until n if i + j == v)
   yield (i, j)

foo(10, 10) foreach {
  case (i, j) =>
    println(s"($i, $j) ")  // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) (6, 4) (7, 3) (8, 2) (9, 1)
}

この例ではn == 10v == 10です。最初のイテレーションではi == 0かつj == 0で、i + j != vとなるので、何も生成されません。 i1にインクリメントされるまでに、jはあと9回インクリメントされます。ifガードがなければ、単純に以下の内容が表示されます。


(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 0) ...

内包表記はリストだけのものではありません。 操作 withFiltermapflatMapを(適切な型で)サポートする全てのデータ型は、シーケンス内包表記で使うことができます。

内包表記の中ではyieldを省略することができます。その場合、内包表記はUnitを返します。 これは副作用をもたらす必要があるときに役立ちます。 こちらは先に出たプログラムと同等のものですが、yieldを使っていません。

def foo(n: Int, v: Int) =
   for (i <- 0 until n;
        j <- 0 until n if i + j == v)
   println(s"($i, $j)")

foo(10, 10)

Contributors to this page: