Scala入门系列(九):函数式编程

小说:农业种植致富好项目作者:建伯更新时间:2019-02-23字数:61377

Scala入门系列(九):函数式编程


引言

Scala是一门既面向对象,又面向过程的语言,Scala的函数式编程,就是Scala面向过程最好的佐证。也真是因此让Scala具备了Java所不具备的更强大的功能和特性。

而之所以Scala一直没有替代Java,一是因为Java诞生早,基于Java开发了大量知名的工程,并且最重要的是Java现在不只是一门编程语言,而是一个庞大的技术生态圈,所以未来十年内Scala也不会完全替代Java,但是Scala会在自己特有的领域大发光彩

将函数赋值给变量

Scala中函数是一等公民,可以独立定义,独立存在,而且可以直接将函数作为值赋值给变量

Scala语法规定:将函数赋值给变量时,必须在函数后加上空格和下划线

scala> def sayHello(name: String) = println("hello, " + name)
sayHello: (name: String)Unit
// 必须在函数后加上空格和下划线
scala> val sayHelloFunc = sayHello _
sayHelloFunc: String => Unit = <function1>
scala> sayHelloFunc("sparks")
hello, sparks

匿名函数

可以直接定义匿名函数并赋值给某个变量;也可以将匿名函数传入其他函数之中。

Scala定义匿名函数的语法规则是,(参数名: 参数类型) => {函数体}

匿名函数在Spark源码中有大量使用。

scala> val sayHelloFunc = (name: String) => {println("hello, "+name)}
sayHelloFunc: String => Unit = <function1>
scala> sayHelloFunc("sparks")
hello, sparks

高阶函数

在Scala中,由于函数时一等公民,因此可以直接将某个函数传入其他函数,作为参数。这个功能是极其强大的,也是Java这种面向对象的编程语言所不具备的。

接收其他函数作为参数的函数,也被称为高阶函数(higher-order function)

scala> val sayHelloFunc = (name: String) => println("hello, "+name)
// 接收函数作为参数
scala> def greeting(func: (String)=> Unit, name: String){func(name)}
// 将匿名函数当做参数传入
scala> greeting(sayHelloFunc, "leo")
hello, leo
// Array的map函数也是高阶函数
scala> Array(1,2,3,4,5).map((num: Int) => num*num)
res5: Array[Int] = Array(1, 4, 9, 16, 25)
// 高阶函数的另外一个功能是将函数作为返回值
scala> def getGreetingFunc(msg: String) = (name: String) => println(msg + " " + name)
scala> val greetingFunc = getGreetingFunc("hello")
scala> greetingFunc("sparks")
hello sparks

高阶函数-类型推断(更易阅读)

在高阶函数中,经常将只需要执行一次的函数定义为匿名函数作为参数传递给高阶函数,就好像map()、filter()等高阶函数中经常能看到使用了匿名函数作为参数。匿名函数在这里有一个特性能够帮助我们写出更容易阅读的函数——参数推断。 
正常情况下,我们使用匿名函数的方式如下:

object Test {
def main(args: Array[String]) {
val arr = Array(1.23, 34.1, 21.32)
val result = arr.map((a: Double) => a * 3)
println(result.mkString(" "))
}
}

即在map函数中定义匿名函数(a: Double) => a * 3,但是由于map函数知道你传入的是一个类型为(Double)=> Double类型的函数,故可以简化为

object Test {
def main(args: Array[String]) {
val arr = Array(1.23, 34.1, 21.32)
val result = arr.map((a) => a * 3)
println(result.mkString(" "))
}
}

并且如果匿名函数只有一个参数,则可以省略(),继续简化:

object Test {
def main(args: Array[String]) {
val arr = Array(1.23, 34.1, 21.32)
val result = arr.map(a => a * 3)
println(result.mkString(" "))
}
}

在此基础上,如果参数在=>右边只出现了一次,则可以用_替换它(Spark中大量使用了这种语法):

object Test {
def main(args: Array[String]) {
val arr = Array(1.23, 34.1, 21.32)
val result = arr.map(_ * 3)
println(result.mkString(" "))
}
}

  
类似的示例:

// 定义该函数参数接收Int参数,返回Int值
scala> def triplt(func: (Int) => Int) = {func(3)}
// 3作为该匿名参数的唯一参数与6做了相乘,然后返回
// 相当于triplt((a: Int) => a * 6)
scala> triplt(_ * 6)
res17: Int = 18
scala> triplt(_ + 6)
res18: Int = 9

常见的高阶函数

  1. map:对传入集合的每个元素进行映射,返回每个元素处理后的集合。
scala> Array(3,4,1,5,2).map(2 * _)
res23: Array[Int] = Array(6, 8, 2, 10, 4)
  1. foreach:对传入的每个元素都进行处理,但是没有返回值。
scala> (1 to 9).map("*" * _).foreach(println _)
*
**
***
****
*****
******
*******
********
*********
  1. filter:对传入的每个元素都进行条件判断,保留True元素。
scala> (1 to 20).filter(_ % 2 == 0)
res20: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
  1. reduceLeft:从左侧开始,即先对参数0和参数1进行处理,然后将结果与元素2处理,再将结果与元素3处理,一次类推,即为reduce;(Spark编程的重点!)
// 相当于1 * 2 * 3 *……* 9
scala> (1 to 9).reduceLeft(_ * _)
res21: Int = 362880
// 注意:reduceLeft的中间结果是传递给第一个参数的
scala> List(1,2,3,4).reduceLeft{ (m, n) => println(m + ": " + n); m - n}
1: 2
-1: 3
-4: 4
res77: Int = -8
// 注意:reduceRight的中间结果是传递给第二个参数的
scala> List(1,2,3,4).reduceRight{ (m, n) => println(m + ": " + n); m - n}
3: 4
2: -1
1: 3
res78: Int = -2
  1. foldLeft:类似于reduceLeft,不过flod提供一个初始值。
scala> List(1,2,3,4).foldLeft(10) { (m, n) => println(m + ": " + n); m - n}
10: 1
9: 2
7: 3
4: 4
res73: Int = 0
scala> List(1,2,3,4).foldRight(10) { (m, n) => println(m + ": " + n); m - n}
4: 10
3: -6
2: 9
1: -7
res74: Int = 8
  1. 对元素进行两两比较,排序
scala> Array(3,4,1,5,2).sortWith(_ < _)
res22: Array[Int] = Array(1, 2, 3, 4, 5)
  1. collect操作,结合偏函数使用

我要说两句: (0人参与)

发布