10.3 For 内包表記の変換
すべての for 内包表記は3つの高階関数 map, flatMap, filter で表現できます。これが翻訳のスキーム(枠組み)であり、Scala コンパイラも使っています。
• 単純な for 内包表記
for (x <- e) yield e'
これは次のように翻訳されます。
e.map(x => e')
• for 内包表記
for (x <- e if f; s) yield e'
ただし f はフィルタで、s は (空でも良い) ジェネレータあるいはフィルタの列。これは次のように翻訳されます。
for (x <- e.filter(x => f); s) yield e'
そして後者の式に対して翻訳が続きます。
• for 内包表記
for (x <- e; y <- e'; s) yield e''
ただし s は (空でも良い) ジェネレータあるいはフィルタの列。これは次のように翻訳されます。
e.flatMap(x => for (y <- e'; s) yield e'')
そして後者の式に対して翻訳が続きます。
たとえば、「和が素数になる整数の組」を例にとると、
for { i <- range(1, n) j <- range(1, i) if isPrime(i+j) } yield {i, j}
この式を翻訳すると次が得られます。
range(1, n) .flatMap(i => range(1, i) .filter(j => isPrime(i+j)) .map(j => (i, j)))
逆に、関数 map, flatMap, filter を for 内包表記で表すこともできます。3つの関数を今度は for 内包表記を使って実装してみます。
object Demo { def map[A, B](xs: List[A], f: A => B): List[B] = for (x <- xs) yield f(x) def flatMap[A, B](xs: List[A], f: A => List[B]): List[B] = for (x <- xs; y <- f(x)) yield y def filter[A](xs: List[A], p: A => Boolean): List[A] = for (x <- xs if p(x)) yield x }
驚くことではありませんが、Demo.map の本体の for 内包表記の翻訳は、クラス List の map を呼び出します。同様に、Demo.flatMap と Demo.filter は、クラス List の flatMap と filterの呼び出しへ翻訳されます。
演習 10.3.1 次の関数を for を用いて定義しなさい。
def flatten[A](xss: List[List[A]]): List[A] = (xss :\ (Nil: List[A])) ((xs, ys) => xs ::: ys)
演習 10.3.2 次を高階関数を用いて翻訳しなさい。
for (b <- books; a <- b.authors if a startsWith "Bird") yield b.title for (b <- books if (b.title indexOf "Program") >= 0) yield b.title