一、偏函数(partial function)
1、提出需求
1. 给你一个集合val list01 = List(1, 2, 3, 4, "abc") ,请完成如下要求:
a): 将集合list中的所有数字+1,并返回一个新的集合;
b): 要求忽略掉 非数字 的元素,即返回的 新的集合 形式为 (2, 3, 4, 5).
模式匹配与filter-map方式解决问题示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object PartialFunTest01 {
def main(args: Array[String]): Unit = {
/*
1. 给你一个集合val list01 = List(1, 2, 3, 4, "abc") ,请完成如下要求:
a): 将集合list中的所有数字+1,并返回一个新的集合;
b): 要求忽略掉 非数字 的元素,即返回的 新的集合 形式为 (2, 3, 4, 5).
*/
val list01 = List(1, 2, 3, 4, "abc")
// 解决方式一:采用filter-map的方式处理,返回一个新的集合
// 缺点:虽然可以解决,但是比较麻烦,需要写好几个函数
val list02 = list01.filter(filterFun).map(mapFun)
println("方式一list02:" + list02)
// 解决方式二:采用match - case(模式匹配)方式处理,返回一个新的集合
// 缺点:虽然写起来比较简单,但是不够优雅完美!
val list03 = list01.map(matchFun)
println("方式二list03:" + list03)
}
def filterFun(ele: Any): Boolean = {
ele.isInstanceOf[Int]
}
def mapFun(num: Any): Int = {
num.asInstanceOf[Int] + 1
}
def matchFun(ele: Any): Any = {
ele match {
case num: Int => num + 1
case _ =>
}
}
}
===============================运行结果===============================
方式一list02:List(2, 3, 4, 5)
方式二list03:List(2, 3, 4, 5, ())
===============================运行结果===============================
2、基本介绍
1. 在对符合某个条件,而不是所有情况进行逻辑操作时,使用偏函数是一个不错的选择;
2. 将包在大括号内的一组case语句封装为函数,我们称之为偏函数,它只对会作用于指定类型的参数或指定范围值
的参数实施计算,超出范围的值会忽略(未必会忽略,这取决于怎样处理);
3. 偏函数在Scala中是一个特质PartialFunction。
示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object PartialFunTest02 {
def main(args: Array[String]): Unit = {
/*
使用偏函数解决如下问题:
1. 给你一个集合val list01 = List(1, 2, 3, 4, "abc") ,请完成如下要求:
a): 将集合list中的所有数字+1,并返回一个新的集合;
b): 要求忽略掉 非数字 的元素,即返回的 新的集合 形式为 (2, 3, 4, 5).
*/
val list01 = List(1, 2, 3, 4, "abc")
/**
* 说明:定义一个偏函数
* 1. PartialFunction[Any, Int]表示偏函数接收的参数类型是Any,返回类型是Int;
* 2. isDefinedAt(x: Any)如果返回true,就会去调用apply构建构建对象实例,如果
* 是false,则直接过滤;
* 3. apply构造器,对传入的值+1运算,并返回(新的集合)。
*/
val partialFun = new PartialFunction[Any, Int] {
override def isDefinedAt(x: Any): Boolean = {
x.isInstanceOf[Int]
}
override def apply(v1: Any): Int = {
v1.asInstanceOf[Int] + 1
}
}
/**
* 说明:使用偏函数
* 1. 如果使用偏函数,则不能使用map高阶函数,而是使用collect高阶函数;
* 2. 偏函数的执行流程
* a): 遍历list01所有元素;
* b): 然后调用 val partialFun定义的偏函数 --> isDefinedAt(list的单个元素) --> 进行判断
* 如果为true,再调用apply(list的单个元素)进行运算,将结果放入到新的集合,如果为false,
* 则直接过滤。
*/
val list02 = list01.collect(partialFun) // List(2, 3, 4, 5)
println("偏函数解决问题list02:" + list02)
}
}
偏函数的小结
1. 使用构建特质的实现类(使用的方式是PartialFunction的匿名子类);
2. PartialFunction 是个特质(看源码);
3. 构建偏函数时,参数形式 [Any, Int]是泛型,第一个表示参数类型,第二个表示返回参数类型;
4. 当使用偏函数时,会遍历集合的所有元素,编译器执行流程时先执行isDefinedAt()如果为true,就会执行 apply,
构建一个新的Int对象返回;
5. 执行isDefinedAt() 为false 就过滤掉这个元素,即不构建新的Int对象;
6. map函数不支持偏函数,因为map底层的机制就是所有循环遍历,无法过滤处理原来集合的元素;
7. collect函数支持偏函数。
偏函数简写形式示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object PartialFunTest03 {
def main(args: Array[String]): Unit = {
/*
使用偏函数简写形式解决如下问题:
1. 给你一个集合val list01 = List(1, 2, 3, 4, "abc") ,请完成如下要求:
a): 将集合list中的所有数字+1,并返回一个新的集合;
b): 要求忽略掉 非数字 的元素,即返回的 新的集合 形式为 (2, 3, 4, 5).
*/
val list01 = List(1, 2, 3, 4, 6.8, "abc")
// 偏函数简写方式一
val list02 = list01.collect(partialFun)
println("方式一list02:" + list02) // List(2, 3, 4, 5, 13)
// 偏函数简写方式二
val list03 = list01.collect{
case ele: Int => ele + 1
case ele: Double => (ele * 2).toInt
case ele: Float => (ele * 3).toInt
}
println("方式二list03:" + list03) // List(2, 3, 4, 5, 13)
}
// 偏函数简写方式一
def partialFun: PartialFunction[Any, Int] = {
// 简写成case语句
case ele: Int => ele + 1
case ele: Double => (ele * 2).toInt
}
}
二、作为参数的函数
1、基本介绍
函数作为一个变量传入到了另一个函数中,那么该作为参数的函数的类型是:function1,
即:(参数类型) => 返回类型。
示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object FunArgsTest01 {
def main(args: Array[String]): Unit = {
// 函数作为参数传给函数使用
val list01 = List(1, 3, 6, 12, 18, 7, 21)
val list02 = list01.map(plus(_)) // List(7, 9, 12, 24, 36, 14, 42)
println("list02:" + list02)
// 在Scala中,函数也是有类型的,比如plus就是<function1>
println("plus函数的类型:" + (plus _)) // <function1>
}
def plus(x: Int): Int = {
if (x > 5) {
x * 2
} else {
x + 6
}
}
}
上述代码小结
1. map(plus(_)) 中的plus(_) 就是将plus这个函数当做一个参数传给了map,_这里代表从集合中遍历出来
的一个元素;
2. plus(_) 这里也可以写成 plus表示对List(1, 3, 6, 12, 18, 7, 21)遍历,将每次遍历的元素传给plus的x进
行3 + x运算后,返回新的Int ,并加入到新的集合list02中;
3. def map[B, That](f: A => B) 声明中的 f: A => B是一个函数。
三、匿名函数
1、基本介绍
没有名字的函数就是匿名函数,可以通过函数表达式来设置匿名函数。
示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object AnonymouseFunTest01 {
def main(args: Array[String]): Unit = {
/**
* 说明:匿名函数
* 1. 不需要写“def 函数名”
* 2. 不需要写返回类型,使用的是类型推导;
* 3. “=” 变成 “=>”写法;
* 4. 如果有多行,则使用{}包括。
*/
val res = (num: Double) => {
println("原有输入的值:" + num)
(3 * num).toInt
}
println("res:" + res(7.8)) // 23
/*
练习:写一个匿名函数,返回两个整数的乘积!
*/
val res02 = (n1: Int, n2: Int) => n1 * n2
println("res02:" + res02(5, 9)) // 45
}
}
四、高阶函数
1、基本介绍
能够接受函数作为参数的函数,叫做高阶函数 (higher-order function)。可使应用程序更加健壮!
示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object HigherOrderFunTest01 {
val map = Map("Jack" -> 23, "Leo" -> 19, "Tom" -> 26)
def main(args: Array[String]): Unit = {
val name = "Jack"
val age = getInfo(getAge, name)
println(name + " --> " + age) // Jack --> 23
/**
* 说明:高阶函数可以返回函数类型
* 1. minusxy是高阶函数,因为它返回匿名函数;
* 2. 返回的匿名函数:(y: Int) => x - y
* 3. 返回的匿名函数可以使用变量接收
*/
// 高阶函数:minusxy
def minusxy(x: Int) = {
(y: Int) => x - y // 匿名函数
}
val f = minusxy(5) // 相当于 val f = (y: Int) => 5 - y
println("f的类型:" + f) // <function1>
println("求值:" + f(9)) // -4
}
// 高阶函数基本使用
// 能够接受函数作为参数的函数,叫做高阶函数 (higher-order function)。
def getInfo(fun: String => Int, name: String): Int = {
fun(name)
}
// getAge 接收一个String类型的参数,返回Int类型
def getAge(name: String): Int = {
map.getOrElse(name, 28)
}
}
五、参数(类型)推断
1、基本介绍
参数推断省去类型信息(在某些情况下[需要有应用场景],参数类型是可以推断出来的,如:
list=(1,2,3) list.map()
map中函数参数类型是可以推断的),同时也可以进行相应的简写。
参数类型推断写法说明
1. 参数类型是可以推断时,可以省略参数类型;
2. 当传入的函数,只有单个参数时,可以省去括号;
3. 如果变量只在=>右边只出现一次,可以用_来代替。
示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object ParameterInferTest01 {
def main(args: Array[String]): Unit = {
val list01= List(1, 3, 6, 10)
// list01每个元素加1
println(list01.map((x: Int) => x + 1)) // List(2, 4, 7, 11)
println(list01.map((x) => x + 1)) // List(2, 4, 7, 11)
println(list01.map(x => x + 1)) // List(2, 4, 7, 11)
// 最后简写
println(list01.map(_ + 1)) // List(2, 4, 7, 11)
// list01所有元素求和
println(list01.reduce(sumFun)) // 20
println(list01.reduce((n1: Int, n2: Int) => n1 + n2)) // 20
println(list01.reduce((n1, n2) => n1 + n2)) // 20
// 最后简写
println(list01.reduce(_ + _)) // 20
}
def sumFun(n1: Int, n2: Int): Int = {
n1 + n2
}
}
六、闭包(closure)
1、基本介绍
闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)。
示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object ClosureTest01 {
def main(args: Array[String]): Unit = {
// 1. 用等价理解方式改写
// 2. 对象属性理解
def minusxy(x: Int) = (y: Int) => x -y
// fun函数就是闭包
val fun = minusxy(20)
println("fun(3):" + fun(3)) // 17
println("fun(6):" + fun(6)) // 14
}
}
上述代码小结
1. 第1点“(y: Int) => x – y”返回的是一个匿名函数,因为该函数引用到函数外的x,那么该函数和x整体形成一个闭包;
如:这里 val fun = minusxy(20) 的fun函数就是闭包
2. 也可以这样理解,返回函数是一个对象,而x就是该对象的一个字段属性,他们共同形成一个闭包;
3. 当多次调用f时(可以理解多次调用闭包),发现使用的是同一个x, 所以x不变;
4. 在使用闭包时,主要搞清楚返回函数引用了函数外的哪些变量,因为他们会组合成一个整体(实体),形成一个闭包。
闭包的最佳实践
需求:编写一个程序,具体要求如下:
1. 编写一个函数 makeSuffix(suffix: String) 可以接收一个文件后缀名(比如.jpg),并返回一个闭包;
2. 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg) ,则返回 文件名.jpg ,
如果已经有.jpg后缀,则返回原文件名;
3. 要求使用闭包的方式完成
4. String.endsWith(xx)
示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object ClosureTest02 {
def main(args: Array[String]): Unit = {
val file_name01 = "java.jpg"
val file_name02 = "scala"
// 闭包函数
val fun = makeSuffix(".jpg")
val file_name_res01 = fun(file_name01)
val file_name_res02 = fun(file_name02)
println(file_name_res01 + " -- " + file_name_res02) // java.jpg -- scala.jpg
}
def makeSuffix(suffix: String) = (file_name: String) => {
if (file_name.endsWith(".jpg")) {
file_name
} else {
file_name + suffix
}
}
}
通过上述实践代码体会闭包的好处
1. 返回的匿名函数和makeSuffix(suffix: string)的suffix变量组合成一个闭包,因为 返回的函数引用到suffix这个变量;
2. 体会一下闭包的好处,如果使用传统的方法,也可以轻松实现这个功能,但是传统方法需要每次都传入 后缀名,
比如 .jpg ,而闭包因为可以保留上次引用的某个值,所以我们传入一次就可以反复使用!
七、函数柯里化(curry)
1、基本介绍
1. 函数编程中,接受多个参数的函数都可以转化为接受单个参数的函数,这个转化过程就叫柯里化;
2. 柯里化就是证明了函数只需要一个参数而已。其实在闭包的练习过程中,已经涉及到了柯里化操作;
3. 不用设立柯里化存在的意义这样的命题。柯里化就是以函数为主体这种思想发展的必然产生的结果,
即:柯里化是面向函数思想的必然产生结果。
示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object CurryTest01 {
def main(args: Array[String]): Unit = {
/*
练习:编写一个函数,接收两个整数,可以返回两个数的乘积,要求:
1. 使用常规的方式完成
2. 使用闭包的方式完成
3. 使用函数柯里化完成
*/
val num01 = 5
val num02 = 7
// 1. 使用常规的方式完成
def mul(n1: Int, n2: Int): Int = {
n1 * n2
}
println("方式1结果:" + mul(num01, num02)) // 35
// 2. 使用闭包的方式完成
def closureMul(n1: Int) = (n2: Int) => n1 * n2
// 闭包函数:
val cmul = closureMul(num01)
println("方式2结果:" + cmul(num02)) // 35
// 3. 使用函数柯里化完成
def curryMul(n1: Int)(n2: Int) = n1 * n2
println("方式3结果:" + curryMul(num01)(num02)) // 35
}
}
函数柯里化最佳实践
1. 比较两个字符串在忽略大小写的情况下是否相等,注意,这里是两个任务:
a): 全部转大写(或小写)
b): 比较是否相等
针对这两个操作,我们用一个函数去处理的思想,其实也变成了两个函数处理的思想(柯里化)
上述实践示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object CurryTest02 {
def main(args: Array[String]): Unit = {
// 方式1:柯里化解决
def curryEq(str01: String)(str02: String) = {
// str01.toUpperCase().equals(str02.toUpperCase())
str01.toUpperCase == str02.toUpperCase
}
val str01 = "English Java"
val str02 = "english JAVA"
println("是否相等:" + curryEq(str01)(str02))
// 方式2:使用稍微高级的用法(隐式类):形式为 str.方法()
def eq(str01: String, str02: String): Boolean = {
str01.equals(str02)
}
implicit class CheckEq(str1: String) {
def getRes(str2: String)(fun: (String, String) => Boolean): Boolean = {
fun(str1.toLowerCase, str2.toLowerCase)
}
}
println("是否相等:" + str01.getRes(str02)(eq)) // true
// 不使用自己写的eq方法, 其中:“_.equals(_)”相当于一个匿名函数
println("是否相等:" + str01.getRes(str02)(_.equals(_))) // true
}
}
八、控制抽象
1、看一个需求
如何实现将一段代码(从形式上看),作为参数传递给高阶函数,在高阶函数内部执行这段代码.,其使用
的形式 如breakable{} 。
示例如下:
var n = 10
breakable {
while (n <= 20) {
n += 1
if (n == 18) {
break()
}
}
}
2、控制抽象基本介绍
1. 控制抽象是这样的函数,满足如下条件:
a): 参数是函数
b): 函数参数没有输入值也没有返回值
入门示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object AbstractControlTest01 {
def main(args: Array[String]): Unit = {
// 入门案例
// myRunInThread就是一个抽象控制
// 没有输入,也没有输出的函数(fun: () => Unit)就是抽象控制
def myRunInThread(fun: () => Unit) = {
new Thread{
override def run(): Unit = {
fun() // 只写了 fun: ()
}
}.start()
}
myRunInThread{
() => println("01开始干活,5秒干完!")
Thread.sleep(5000)
println("01干完活了!")
}
// 简写形式,省略()
def myRunInThread02(fun: => Unit): Unit = {
new Thread{
override def run(): Unit = {
fun // 只写了 fun
}
}.start()
}
myRunInThread02 {
println("02开始干活,5秒干完!")
Thread.sleep(5000)
println("02干完活了!")
}
}
}
进阶示例代码:
package com.lj.scala.funcationmost
/**
* @author Administrator
* @create 2020-03-18
*/
object AbstractControlTest02 {
def main(args: Array[String]): Unit = {
// scala自己的while函数
var num = 10
while(num > 0) {
num -= 1
println("while num:" + num)
}
// 进阶用法:实现类似while的myWhile函数
/**
* 说明:
* 1. 函数名为myWhile,实现了类似while循环的效果
* 2. condition: => Boolean是一个没有输入值,返回Boolean类型函数
* 3. block: => Unit没有输入值,也没有返回值的抽象控制函数
*/
def myWhile(condition: => Boolean)(block: => Unit): Unit = {
// 类似while循环,递归
if (!condition) {
block // 9 8 7 6 .....
myWhile(condition)(block)
}
}
num = 10
myWhile(num == 0) {
// block 代码块!
num -= 1
println("myWhile num:" + num)
}
}
}
对以前的知识回顾,加深基础知识!
学习来自:北京尚硅谷韩顺平老师—尚硅谷大数据技术之Scala
每天进步一点点,也许某一天你也会变得那么渺小!!!