开发者学堂课程【Scala 核心编程 - 进阶:高阶函数和 Map 映射】学习笔记,与课程紧密连接,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/610/detail/9048
高阶函数和 Map 映射
内容介绍:
一、map 映射操作
二、高阶函数的基本使用
三、使用 map 映射函数来解决
四、深刻理解 map 映射函数的机制-模拟实现
一、map 映射操作
上节提出的问题,其实就是一个关于集合元素映射操作的问题。
在 Scala 中可以通过 map 映射操作来解决:将集合中的每一个元素通过指定功能就是函数,函数是一系列代码完成一个功能的集合,映射(转换)成新的结果,集合这里其实就是所谓的将函数作为参数传递给另外一个函数,这是函数式编程的特点。
1.以 HashSet 为例说明
def map[B](f:(A)→ B): HashSet[B] //map 函数的签名
1)这个就是map映射函数集合类型都有
2)[B]是泛型
3) map 是一个高阶函数(高阶函数是可以接收一个函数的函数),可以接收函数f:(A)=>B后面详解(先简单介绍下),接收函数(A)的类型是可以传入一个值,这个值是泛型,也可以返回一个值,这个值的也是泛型(B)
4)HashSetTB1就是返回的新的集合
二、高阶函数的基本使用
1.代码及说明
object HighorderFunDemoo1 {
def main( args : Array [string]): unit = {
//使用高阶函数
val res = test(sum2,3.5)
println(“res=”+res)
//传入一个函数,所以无需在 sum2 后面写小括号,sum() 代表执行
}
//说明:
//1. test就是一个高阶函数
//2. f: Double =>Double 表示一个函数,该函数可以接受一个//DoubLe,返回DoubLe
//3. ni : Double是普通参数
//4. f(n1) 代表在 test 函数中执行要传入的函数,要传入的函数要//满足下面函数的签名
//可以理解为,如果画一个底层的画,类似于 f 指向了这个函数的地//址,之后对函数参数进行使用
def test (f : Double => Double,n1: Double) = {
f(n1)
}
//普通的函数
def sum2 (d : Double) : Double = {
//这个函数满足上面签名的需要
d + d
}
}
运行结果为:
res=7.0
在该程序中,sum2 这个函数被调用,说明该程序中一个函数调用了另一个函数,如何理解?
下面的示意图帮助理解传函数这种参数的一种机制。
这是一块内存,有三个核心区块,栈区、堆区、方法区。
以下两个函数属于一个类,先后被加载
def test (f : Double => Double,n1: Double) = {
f(n1)
}
当在栈中执行下面的代码后
val res = test(sum2,3.5)
sum2 的地址传给了 f ,相当于进入了下面的代码中
def sum2 (d : Double) : Double = {
d + d
}
可以理解为 f 指向 sum2 这段代码块,如何f(n1) 会调用下面代码的内容
def sum2 (d : Double) : Double = {
d + d
}
(简单理解为 f 是指向 sum2 的地址,函数本身也有地址,函数是共用的)
2.另一种传递方式
有时可以看到部分程序员这样传递 :val res = test(sum2_,3.5)
这样传递运行结果不变(下划线代表将一个函数传递)
如何理解这种传递方式,下面的案例说明帮助理解:
object HighorderFunDemoo1 {
def main( args : Array [string]): unit = {
val res = test(sum2,3.5)
println(“res=”+res)
//在 Scala 中也可以直接将一个函数赋给一个变量
//如果这里是val f1= myPrint ,则相当于执行这个函数将 //myPrint 的结果返回 f1,在很多情况下,想把一个函数赋给一个//变量,但是不想执行这个变量,所以可以用下划线操作,
如下
v
al f1= myPrint
_
//将一个函数赋给一个变量但是不执行该函数,此时 f1 的类型是一个没有形参 Unit 函数
}
d
ef myPrint(): Unit={
p
rintln(“hello,world!”)
def test (f : Double => Double,n1: Double) = {
f(n1)
}
def sum2 (d : Double) : Double = {
println
(“sum2
被调用
”)
d + d
}
}
运行结果为:
sum2 被调用
res=7.0
发现没有输出 hello,world! ,说明实现了将一个函数赋给一个变量但是不执行该函数
3.为进一步理解,下面举一个高阶函数的案例
//传入无参的高阶函数
object HighOrderFunDemo02 {
def main(args: Array [string]) : unit = {
}
//如果想让高阶函数接收一个无参的函数,写法为如下
//说明 test2 是一个高阶函数,可以接收一个没有输入,返回为 //Unit 的函数
def te
s
t2(f: () => unit) = {
f()
}
def sayOK ()= {
printLn( "sayOKKK...")}
}
}
运行结果为:
sayOKKK...
注意:
传入的类型要匹配,比如需要传入一个无参的函数,如果传入其他函数会报错,
举个例子:
object HighOrderFunDemo02 {
def main(args: Array [string]) : unit = {
t
est2(sayOK)
t
est2(sub)
//此时会报错,期望函数与实际传入函数不匹配
}
def te
s
t2(f: () => unit) = {
f()
}
def sayOK ()= {
printLn( "sayOKKK. ..")}
}
d
ef sub(n1:Int):Unit={
}
}
三、使用 map 映射函数来解决
object MapoperateDemoo2 {
def main(args: Array [string]): unit = {
/*
请将List(3,5,7)中的所有元素都*2,
将其结果放到一个新的集合中返回,即返回一个新的List(6,10,14),请编写程序实现.
*/
val list = List( 3,5,7)
//说明:
// list.map(multiple) 做了什么
//1.将 list 集合的元素依次遍历
//2.将各个元素传递给 multiple 函数=>新 Int
//3.将得到新的Int ,放入到应该新的集合并返回
//4.因此 multiple 函数会被调用3次,如果元素个数更多,则被调//用的次数也更多
list.map(multiple)
val
list2=list.map(multiple)
println(“list2=”+list2) //
此时应该返回为 list(
6,10,14)
}
def multiple(n: Int ) : Int = {
2* n
}
}
运行结果为:list(6,10,14)
证明说明4:
注意:
map 其实会接收两个值,B 和 That 都是泛型,如下图可知,可以接收一个 Int 类型返回一个 B 类型的函数。
四、深刻理解 map 映射函数的机制-模拟实现
//底层代码
object MapoperateDemoo2 {
def main(args: Array [string]): unit = {
val list = List( 3,5,7,9)
list.map(multiple)
val
list2=list.map(multiple)
println(“list2=”+list2)
val
myList=MyList
println(myList)
}
def multiple(n: Int ) : Int = {
2* n
}
}
class MyList{
v
al list1=List(3,5,7,9)
//这里可以写一个构造器,动态的传入数据,因为时间关系,将数据//固定并且都为整型
val list2=List
[
Int]()
//新的空集合
//写 map
//注意 map 最后返回的结果是一个集合,def map():Unit 括号里//面可以是泛型的函数
def map(Int=>Int):List[Int]={
//接收一个整数再返回一个整数
//遍历集合
for(item<
-this.list1
)
//item
此时是集合里面的各个元素
list2=list2:+f(item) //
f
传入item
}
list
2
}
}
o
bject MyList{
d
ef apply(): MyList =new MyList()
}
运行结果为:
list
2= List(6,10,14,18)
c
om.atguigu.chapter11.MyList$@7f3425a
说明该机制没问题,接下来用该机制来完成
object MapoperateDemoo2 {
def main(args: Array [string]): unit = {
val list = List( 3,5,7,9)
list.map(multiple)
val
list2=list.map(multiple)
println(“list2=”+list2)
val
myList=MyList
(
)
val
myList2=myList.map
(
multiple)
println(“myList2”+ myList2)
}
def multiple(n: Int ) : Int = {
2* n
}
}
class MyList{
v
al list1=List(3,5,7,9)
val list2=List
[
Int]()
def
map(Int=>Int):List[Int]={
for(item<
-this.list1
)
//item
此时是集合里面的各个元素
list2=list2:+f(item) //
f
传入item
}
list
2
}
}
o
bject MyList{
d
ef apply(): MyList =new MyList()
}
运行结果为:
myList
2= List(6,10,14,18)
到此已经将底层模拟机制完成,但是这段代码和真正的底层还是有区别,真正的底层可能做了一些优化,算法处理。
这里是直接的遍历处理,将来下面这段代码会有很多工作:
def
map(Int=>Int):List[Int]={
for(item<
-this.list1
)
list2=list2:+f(item)
}
Scala 这种机制可以做很多工作,比如过滤(遍历过后再进行过滤),或者扁平化。