函数式编程是scala的最大魅力之所在,在函数式编程语言中,函数是“头等公民”,可以像任何其他数据类型一样被传递和操作。因为Scala混合了面向对象和函数式的特性,所以对Scala来说,函数是“头等公民”。函数可以不依赖于类、对象或者接口而单独存在,函数可以作为函数的参数或作为函数的返回值。使用函数式编程可以简化复杂算法,并且代码量更少。Spark中的所有计算基本上都是用函数式编程来做的。
一、作为值的函数
fun的类型是(Double)=>Double,意为接受Double参数并返回Double的函数。能够对fun做的有:调用,传递。fun调用:
scala>def fun1(name:String){println(name)}
fun1:(name: String)Unit
scala>val fun1_v = fun1 _
fun1_v:String => Unit = <function1>
scala>fun1_v("hello func")
hellofunc
可以将函数赋值给变量
二、匿名函数
为了算法的简洁性我们可能不需要函数的名称,只需要函数体对我们输入的数据进行处理,这个时候就不需要为函数命名。
fun2是一个变量,函数的参数是String类型,返回类型时Unit。fun2中的=>符号可以看做是创建函数实例的语法糖。例如:A => T,A,B => T表示一个函数的输入参数类型是“A”,“A,B”,返回值类型是T。上述定义的匿名函数,其实是下面这种写法的简写:
备注:任何函数值都是某个扩展了scala包的若干个FunctionN特质之一的类的实例,如Function0是没有参数的函数,Function1是有一个参数的函数等等。每个FunctionN特质有一个apply方法来调用函数。
- trait Function2[@specialized(scala.Int, scala.Long, scala.Double) -T1, @specialized(scala.Int, scala.Long, scala.Double) -T2, @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double) +R] extends AnyRef { self =>
- /** Apply the body of this function to the arguments.
- * @return the result of function application.
- */
- def apply(v1: T1, v2: T2): R
- /** Creates a curried version of this function.
- *
- * @return a function `f` such that `f(x1)(x2) == apply(x1, x2)`
- */
- @annotation.unspecialized def curried: T1 => T2 => R = {
- (x1: T1) => (x2: T2) => apply(x1, x2)
- }
- /** Creates a tupled version of this function: instead of 2 arguments,
- * it accepts a single [[scala.Tuple2]] argument.
- *
- * @return a function `f` such that `f((x1, x2)) == f(Tuple2(x1, x2)) == apply(x1, x2)`
- */
- @annotation.unspecialized def tupled: Tuple2[T1, T2] => R = {
- case Tuple2(x1, x2) => apply(x1, x2)
- }
- override def toString() = "<function2>"
- }
三、带函数参数的函数(高阶函数)
Rdd中的map方法:
- def map[U: ClassTag](f: T => U): RDD[U] = withScope {
- val cleanF = sc.clean(f)
- new MapPartitionsRDD[U, T](this, (context, pid, iter) => iter.map(cleanF))
- }
函数的函数的返回值是一个函数:
Scala集合库提供的高阶函数map、zip、flatMap:
高阶函数的变换:
scala> def spark(func: (String) =>Unit, name: String) = func(name)
spark: (func: String => Unit, name:String)Unit
scala> spark((name:String) =>println(name) , "spark")
spark
scala> spark( name => println(name) ,"spark") //scala类型推断去掉参数类型
spark
scala> spark(println(_),"spark") //只有一个参数,用下划线代替
spark
scala> spark(println _,"spark") //部分应用函数,可以使用单个下划线替换整个参数列表
spark
scala> spark(println, "spark") //只有一个参数,可以省略
Spark
scala> (1 to 3) map( _ * 2)
res8:scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6)
四、闭包
这里addMore 函数捕获了外部变量more。
五、柯里化(Currying)
1)柯里化指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数作为参数的函数。
scala> def sum_Currying(x: Int) = (y:Int) => x + y
sum_Currying: (x: Int)Int => Int
scala> sum_Currying(45)(4)
res15: Int = 49
通过柯里化变成了两个函数的连续调用,可以看做链式的过程。
2)可以利用柯里化把某个函数参数单独拎出来,提供更多用于类型推断的信息。
注:本学习笔记来自DT大数据梦工厂