10.2 For 内包表記によるクエリ

(Querying with For-Comprehensions)

for 内包表記は、データベースのクエリ言語による一般的操作と本質的に同じです。たとえば、データベース books があり、本のリストとして表現されているとしましょう。ただし Book は次のように定義されるものとします。

case class Book(title: String, authors: List[String])

次はデータベースの小さなサンプルです。

val books: List[Book] = List( Book("Structure and Interpretation of Computer Programs", List("Abelson, Harold", "Sussman, Gerald J.")), Book("Principles of Compiler Design", List("Aho, Alfred", "Ullman, Jeffrey")), Book("Programming in Modula-2", List("Wirth, Niklaus")), Book("Introduction to Functional Programming"), List("Bird, Richard")), Book("The Java Language Specification", List("Gosling, James", "Joy, Bill", "Steele, Guy", "Bracha, Gilad")))

このとき、著者の姓が "Ullman" である本すべての書名を探すには、

for (b <- books; a <- b.authors if a startsWith "Ullman") yield b.title

(ここで startsWith は java.lang.String のメソッド)。あるいは書名が "Program" で始まる本の書名を探すには

for (b <- books if (b.title indexOf "Program") >= 0) yield b.title

あるいは、少なくとも二冊の本を書いたすべての著者の名前をデータベース上で探すには、

for (b1 <- books; b2 <- books if b1 != b2; a1 <- b1.authors; a2 <- b2.authors if a1 == a2) yield a1

最後のコードはまだ完全ではありません。なぜなら著者が結果リストに複数回現れるからです。結果リストから重複した著者を除く必要があります。これは次の関数を使えばできます。

def removeDuplicates[A](xs: List[A]): List[A] = if (xs.isEmpty) xs else xs.head :: removeDuplicates(xs.tail filter (x => x != xs.head))

メソッド removeDuplicates の最後の式は for 内包表記を使って、等価に次のように表現できます。

xs.head :: removeDuplicates(for (x <- xs.tail if x != xs.head) yield x)