kmizuの日記

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

for-comprehensionはどのように展開されるか

val seq = for(x <- 1 to 100; y <- 1 to 100; if y == x * x) yield (x, y)

実は,map,flatMap,filterのシンタックスシュガーらしい.

どーいう風に展開されてるのか,知りたい.

http://d.hatena.ne.jp/matt-k/20080511#p2

実は、Scala勉強会当日にも同じ質問が出ました(そのときはScala Language SpecificationのPDFを手元に置いてなかったので、うろ覚えの知識でなんとか展開形を書きましたが)。

詳細については、Scala Language Specificationの6.19 For-Comprehensionsに書かれているので、それを読んでみるのが手っ取り早いと思いますが、とりあえず、上記の式について6.19 For-Comprehensionsに書かれている変換方法に従って展開してみることにします。

まず、

for(x <- 1 to 100; y <- 1 to 100; if y == x * x) yield (x, y)

の部分が、

A for-comprehension

for (p <- e; p' <- e' ...) yield e''

, where ... is a (possibly empty) sequence of generators or guards, is translated to

e.flatmap { case p => for (p' < e' . . .) yield e'' }

に当てはまるので、

(1 to 100).flatMap{ case x => for(y <- 1 to 100; if y == x * x) yield (x, y) }

となります。次に、

y <- 1 to 100; if y == x * x

の部分が、

A generator p <- e followed by a guard if g is translated to a single generator p <- e.filter((x1, . . . , xn) => g ) where x1, ..., xn are the free variables of p.

に当てはまるので、

(1 to 100).flatMap{ case x => for(y <- (1 to 100).filter((y) => y == x * x)) yield (x, y) }

となります。最後に、

for(y <- (1 to 100).filter((y) => y == x * x)) yield (x, y)

の部分が

A for-comprehension for (p <- e) yield e' is translated to e.map { case p => e' }.

に当てはまるので、展開結果は

(1 to 100).flatMap{ case x => (1 to 100).filter((y) => y == x * x).map{ case y => (x, y) } }

となります。