前言
scala中的for相比于java而言非常的灵活多变,同时功能也非常强大。
众所周知,在java或者其他传统的语言中,大多数情况下for只是用来做循环遍历的用途。但是在scala中for却是有许多独特的用法。
for表达式的一般形式
1
| for (sequence) yield expression
|
此处的sequence由生成器、定义、过滤器组成。比如:
1 2
| val list = List.range(1, 11) val sub2 = for (i <- list; if i % 2 == 0) yield 2 * i
|
此处的圆括号也可以用花括号来代替:
1 2 3 4
| for { i <- list if i % 2 == 0 } yield 2 * i
|
for表达式的转译
此处所说的转译指的是编译器将我们所写的for表达式中的生成器、过滤器等转译为等价的高阶函数的过程。
带yield的for转译
带yield的for表达式都会被转译为高阶函数 map
,flatMap
,filter
的组合调用。
比如:
1 2 3 4
| val sub2=for { i <- list if i % 2 == 0 } yield 2 * i
|
将会被转译为:
1
| val sub2 = list.filter(_ % 2 == 0).map(_ * 2)
|
不带yield的for转译
不带yield的for表达式都会被转译为对高阶函数 filter
和foreach
的组合调用。
比如:
1 2 3 4 5 6
| for { i <- list if i % 2 == 0 } { println(i * 2) }
|
将会被转译为:
1
| list.filter(_ % 2 == 0).foreach(i => println(i * 2))
|
for表达式的适用场景
对于scala本身提供的类型比如List和Array,对其使用for表达式的情况很常见,但通常我们很少问为什么?
就像java中增强的for循环的应用一样:
1 2 3
| for(String s : names){ }
|
对java而言,这里的names可以是什么类型???
事实上,for表达式的适用场景不仅是针对于Array和List。
scala提供的其他类型比如Range、Iterator、Stream、Set等一样可以使用for表达式。
比如:
1 2
| for (i <- Range(1, 3)) println(i)
|
正是由于for表达式的转译仅仅依赖于 map
,flatMap
,filter
,foreach
这几个有限数量的高阶函数。
所以for表达式的大规模的适用场景就是理所当然的了。
一般而言,有如下规律:
假设T
为你的某种自定义类型:
- 如果T同时定义了 map , flatMap , filter , foreach
- T只定义了map
- T定义了map和flatMap
- T定义了foreach
- T定义了filter
此时的T的定义可能如下所示:
1 2 3 4 5 6
| abstract class T[A]{ def map[B](f:A=>B):T[B] def flatMap[B](f:A=>T[B]):T[B] def filter(p:A=>Boolean):T[A] def foreach(f:A=>Unit):Unit }
|
例子
最后以书籍《Programming in Scala》中的一个非常经典的例子来结尾。
题意
有一个代表书籍的内存数据库列表如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| case class Book(title: String, authors: String*)
object Book { def books: List[Book] = { val books: List[Book] = List( Book( "Structure and Interpretation of Computer Programs", "tom", "cat"), Book( "Principles of Compiler Design", "tom", "apache"), Book( "The Java Language Specification", "Gosling, James", "Joy, Bill", "Steele, Guy", "Bracha, Gilad"), Book( "Programming in Modula-2", "tom", "spark"), Book( "Elements of ML Programming", "tom", "cat") ) return books } }
|
要求一
找出姓”Gosling”的作者的所有书籍
1 2 3 4 5 6
| val result = for { b <- books a <- b.authors if a.startsWith("Gosling") } yield b.title println(result)
|
要求二
找出所有书名中包含Programming的书籍的书名
1 2 3 4
| val names = for (b <- books if b.title.indexOf("Programming") >= 0) yield b println(names)
|
要求三
找出写了两本书的作者的名字
1 2 3 4 5 6 7 8
| val authors = for { b1 <- books b2 <- books if b1 != b2 a1 <- b1.authors a2 <- b2.authors if a1 == a2 } yield a1 println(authors)
|
注意,此处的结果或会重复。