前言
原本,在撰写本篇文章之前应该先详细列举一下scala的函数式编程,以及其中的map,flatMap,filter以及reduce等等高级函数。但是转念一想。由于高级函数的写法十分简洁。对没有深入了解的朋友可读性并不高,与其令各位朋友看得云里雾里。不如先从for推导式出发。有了这种思想。以便之后的高级函数的理解与运用。
何为for推导式
scala中的for与Java中不同。它拥有比Java中更加强大且丰富的功能。比如他可以在for语句内部添加条件语句。或者有多个定义语句。甚至可以将每层for循环得到的结果最终整合为一个集合。看到这里,相信许多朋友就依然心驰神往。那么废话不多说。咱们从零开始了解Scala for推导式:
- for表达式:
通常,for表达式的形式为:for( seq ) yield expr
其中:seq由一个或多个生成器、定义和过滤器组成序列。以分号隔开(如果用花括号代替小括号,则分号可选)。在scala中,只要for循环的循环体以yield开头,则表示该循环得到的每一个参数都将合并为一个集合。
eg.
case class Book(title : String , authors : String* ) //声明书本类
val books :List[Book] = //建立测试数据
List(
Book(
"Structure and Interpretation of Computer Programs",
"Abelson,Harold","Sussman,Gerald J."
),Book(
"Principles of Compiler Design",
"Aho,Alfred","Ullman,Jeffrey"
),Book(
"Elements of ML Programming",
"Ullman,Jeffrey"
),Book(
"The Java Language Specification","Gosling,James",
"Joy,Bill","Steele,Guy","Bracha,Gilad"
)
)
//寻找姓Ullman的作者
val title=for(b <- books ;a:String <- b.authors ;result=b.title ;if a.startsWith("Ullman"))
yield result
println(title)
看着这个for语句。可能会让人感觉莫名其妙。不着急,且听我慢慢道来。
首先,b <- books
与a <- b.authors
都属于生成器,可以看见scala的for中可以出现多个生成器b与a会逐一迭代books与b.authors。这就折磨脑细胞了——到底执行的时候是如何执行的呢?其实很简单,只要你将其看为两层for循环,后面的生成器在内循环而前面的在外循环,这样肯定就清晰了——前者每执行一个,后者执行一轮。
其次,result=b.title
属于定义,这个应该不用多说。
最后if a.startsWith("Ullman")
属于过滤器,他能过滤掉if语句为false的情况。
因此,上例的结果为:
List(Principles of Compiler Design, Elements of ML Programming)
看到这里,想必大家都能感觉到for推导式的神奇与轻便。其实这得益于scala的函数式编程,实际上scala在编译for推到式的时候,会将其转译为map,flatMap等高等函数,这些我们之后再进行讨论。
根据上例,我们可以看到for推导式很方便的可以用于查询。接下来我们承接上例,再来几个小例子。
for推导式查询
//寻找书名中包含Program的书名
val bookNameTitle=for(b <- books if (b.title.indexOf("Program") >=0 ))
yield b.title
println(bookNameTitle)
这个例子十分简单,可以发现这里省略了定义,这是由于之后用于聚集的参数是b的元素,所以便可以省略。
//寻找写过两本书的作者
val author=for(b1 <- books ;b2 <- books if(b1!=b2);
a1 <- b1.authors ; a2 <- b2.authors if(a1==a2))
yield a1
println(author)
println(removeDuplicates(author))
这段代码便要复杂挺多,但其实熟悉了也就那么回事,将其以Java中的for循环作为对比就是——首先两层for循环,外层为b1,内层为b2,内层循环体中有if语句,为true再来两层for循环,与外面的同理。同时,由这段代码可以深刻的感受到,for推导式节省了太多的冗余代码。令代码更加简洁,可读性强。还是那句话——scala真是一门优雅的语言。
for表达式的转译
前文说到,for表达式可以转译为高级函数。这里我们来“详细”的概述一下:
首先,假设有如下for表达式:
for(x<-expr1) yield expr2 //这里的exprs都是表达式的缩写,只做示例用,以下皆同
这种表达式可以转译为:
expr1.map(x => expr2) //循环expr1中的所有元素,并返回进行操作后的元素
看了注释便能发现,其实两个功能一致
接下来转译一个带过滤器的表达式:
for(x<-expr1 if expr2 ) yield expr3
这个表达式有了上文的基础很好理解。他可以转为下面的表达式:
//首先转译if得
for(x <- expr1 filter( x => expr2) ) yield expr3
//filter用于过滤掉expr2返回false的情况
//接下来转译外面得
expr1.filter(x => expr2).map(x => expr3)
十分简单,不过多废话。接下来看看转译多个生成器的情况:
for(x<-expr1 ; y <- expr2 ) yield expr3
如果我们将其看为两层循环,可以发现。实际上是外循环再不断的像每单个元素添加东西。因此可以如此转译:
//首先转译外循环:
expr1.flatMap(x => for (y <- expr2) yield expr3)
//再转译内循环:
expr1.flatMap(x => for expr2.map(y => expr3))
最后的废话
Scala的for循环表达式是十分轻便且好用的。假设一个程序员能熟练的使用,那它将带来不一样的编程体验。而令更多的程序员同伴了解这种方式是我本篇博客的目的之一,还有另外一个目的便是提出高级函数与函数式编程,以便能以更轻松的方式理解与接受函数式编程。真正有一种——他看似复杂,看似莫名其妙。其实静静看来,我能明白它的含义的感觉。
码字不易,转发请注明出处,谢谢:http://blog.youkuaiyun.com/qq_28945021/article/details/52173307