scala的函数式编程(二)

参数类型推导

scala会尽可能地推导出函数的类型,使得scala代码非常简洁

//参数类型推断
Array(1,2,3,4).map((x : Int) => x * 3)
//map方法会推断出要出入一个 Int => Int 类型的函数,因此可以省略函数参数的类型,写成:
Array(1,2,3,4).map((x) => x * 3)
//当只有一个参数时,可以省略参数的(),写成:
Array(1,2,3,4).map(x => x * 3)
//当只有一个参数时,且在函数体中(也就是=>右边)参数只出现一次,可以用“_”代替参数,写成:
Array(1,2,3,4).map(_ * 3)

这种情况必须是在类型已知的情况下才能使用,也就是说参数类型的推断,必须要有推断依据,正如上面的例子Array的元素,也就是“1,2,3,4”作为函数参数x类型推断的依据。像下面这样没有任何依据的推断,会让scala懵逼、报错


scala> val a = _ * 3
<console>:11: error: missing parameter type for expanded function ((x$1: <error>
) => x$1.$times(3))
       val a = _ * 3
               ^
//正确的表达形式如下:

scala> val b = (_ : Int) * 3
b: Int => Int = $$Lambda$1112/1180105925@7fd751de

scala> val c : (Int) => Int = _ * 3 //这里需要注意":",不推荐使用
c: Int => Int = $$Lambda$1115/1570165812@7283877

一些常用的高阶函数

学习了高阶函数,我们就要使用它们,在这里我再次强调无论是Spark中存在着大量的高阶函数,不把这高阶函数弄懂,学习Spark源码是很难很难的。

接下来介绍的一些用法,在使用Spark的Transformation和Action是很常见的,同时也感受一下,函数是变成的魅力吧。

map: 对传入的每个元素都进行映射,返回一个处理后的元素
foreach: 对传入的每个元素都进行处理,但是没有返回值

scala> Array(1,2,3,4).map(_ * 3)
res9: Array[Int] = Array(3, 6, 9, 12)

scala> Array(1,2,3,4).map(_ * 3).foreach(println _)
3
6
9
12

filter: 对传入的每个元素都进行条件判断,如果对元素返回true,则保留该元素,否则过滤掉该元素

scala> (1 to 20).toArray.filter(_ % 2 == 0).foreach(println)
2
4
6
8
10
12
14
16
18
20

reduceLeft:接收一个二元参数的函数(两个参数),并将它对应到集合中从左到右的所有元素。

scala> (1 to 20).reduceLeft(_ + _)
res13: Int = 210

sortWith: 也是接收一个二元参数的函数,对元素进行两两相比,进行排序

//升序
scala> Array(6,3,2,8,5,3,6,6,7).sortWith(_ < _)
res14: Array[Int] = Array(2, 3, 3, 5, 6, 6, 6, 7, 8)
//降序
scala> Array(6,3,2,8,5,3,6,6,7).sortWith(_ > _)
res15: Array[Int] = Array(8, 7, 6, 6, 6, 5, 3, 3, 2)

柯里化

柯里化(Currying)指的是将原来接收两个参数的函数变成新的接收一个参数的函数的过程。新的函数返回一个以原有第二个参数作为参数的函数。原先调用两个参数的函数变成了调用两次一个参数的函数。下面举一个例子就一目了然了,还用上一节scala的函数式编程(一)的例子来看一下。


scala> def greetingFunc(msg: String) = (name: String) => println(msg + ", " + name)
greetingFunc: (msg: String)String => Unit

//普通的调用方式
scala> val a = greetingFunc("hello")
a: String => Unit = $$Lambda$1254/937592636@dcdf733

scala> a("liumingxin")
hello, liumingxin

//柯里化
scala> greetingFunc("hello")("liumingxin")
hello, liumingxin

特殊处理(控制抽象)

如果我们定义一个方法,这个方法的参数是一个无参且返回Unit对象的函数,就像下面这样:

scala> def greetingFunc(msg : () => Unit) = {println("hello,sid");msg()}
greetingFunc: (msg: () => Unit)Unit

调用这个方法,是这样的:

scala> greetingFunc(() => println("liumingxin"))
hello,sid
liumingxin

在方法的调用过程中,需要再()中定义一个抽象函数,这样写丑吧吧唧的,可读性太差。

要想在调用的过程中省掉"()=>",可以使用换名调用表示法:在参数声明和调用该函数的地方略去"()",但声明时保留"=>"

scala> def greetingFunc(msg : => Unit) = {println("hello,sid");msg}
greetingFunc: (msg: => Unit)Unit

scala> greetingFunc(println("liumingxin"))
hello,sid
liumingxin

这里有一个小细节需要注意,也是我再编写上面代码时遇到的一点小问题,我写两个方法咱们对比一下:

少了个括号,两者的结果却截然不同。 这里需要复习和强调一下,

强调的是:在不适用换名调用表示法的时候,“函数”和“函数()”是有本质的区别

复习的是:方法体或函数体的最后一行代码代表方法或函数的返回值

猜你喜欢

转载自blog.csdn.net/lazy_moon/article/details/81973886