Scala学习日志(三)——轻便神奇的for推导式

来源:互联网 发布:英文软件翻译工具 编辑:程序博客网 时间:2024/06/14 06:04

前言

原本,在撰写本篇文章之前应该先详细列举一下scala的函数式编程,以及其中的map,flatMap,filter以及reduce等等高级函数。但是转念一想。由于高级函数的写法十分简洁。对没有深入了解的朋友可读性并不高,与其令各位朋友看得云里雾里。不如先从for推导式出发。有了这种思想。以便之后的高级函数的理解与运用。

何为for推导式

scala中的for与Java中不同。它拥有比Java中更加强大且丰富的功能。比如他可以在for语句内部添加条件语句。或者有多个定义语句。甚至可以将每层for循环得到的结果最终整合为一个集合。看到这里,相信许多朋友就依然心驰神往。那么废话不多说。咱们从零开始了解Scala for推导式:

  1. 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 <- booksa <- 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.csdn.net/qq_28945021/article/details/52173307

0 0
原创粉丝点击