Scala-07:函数式编程
什么是函数式编程 ?
解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
例如:请求->用户名、密码->连接 JDBC->读取数据库
Scala 语言是一个完全函数式编程语言。万物皆函数。
函数的本质:函数可以当做一个值进行传递
在 Scala 中函数式编程和面向对象编程完美融合在一起了
一、Scala的函数基础
1:Scala的函数语法
定义函数的关键字 函数名(参数名 : 参数类型) : 返回值类型 = {
函数体
}
2:函数与方法的区别
(1)为完成某一功能的程序语句的集合,称为函数。
(2)类中的函数称之方法。
准确的来说,Java中常常把函数和方法混用,认为函数就是方法,方法就是函数。在Scala中,将函数和方法中进行了区分,类中的函数才称之为方法,在Scala 语言可以在任何的语法结构中声明任何的语法 ,函数没有重载和重写的概念;方法可以进行重载和重写
def main(args:Array[String]):Unit = {
def say( name : String ) : Unit = {
println("hello , " + name)
}
say("tom")
}
上面这种写法在main中声明一个函数的使用方法在java中是肯定不能使用的,而在scala中就可以使用
函数的声明最主要的两个要素:参数和返回值
参数:
- 可以没有参数
- 可以有一个参数
- 可以有多个参数
返回值
- 可以有返回值
- 可以无返回值
//没有参数也没有返回值
def test1() : Unit = {
println("这是一个没有参数也没有返回值的函数")
}
//有一个参数没有返回值
def test2( arg : String) : Unit = {
println("这是一个有一个参数没有返回值的函数,参数是:" + arg)
}
//有多个参数没有返回值的函数
def test3( arg1 : Int , arg2 : Int ) : Unit = {
println("这是一个有多个参数没有返回值的函数,参数是:" + arg1 + arg2)
}
//没有参数有返回值
def test4() : String = {
return "这是一个没有参数又返回值得函数"
}
//有一个参数有返回值
def test5( arg : String ) : String {
return "这是一个有一个参数有返回值的函数,参数是:" + arg
}
//有多个参数又返回值的函数
def test6(arg1 : String ,arg2 : String) : String {
return "这是一个有多个参数有返回值的函数,参数是:" + arg1 + arg2
}
3:函数参数
(1)可变参数
(2)如果参数列表中存在多个参数,那么可变参数一般放置在最后
(3)参数默认值,一般将有默认值的参数放置在参数列表的后面
(4)带名参数
4:函数至简原则
(1)return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
(2)如果函数体只有一行代码,可以省略花括号
(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
(4)如果有 return,则不能省略返回值类型,必须指定
(5)如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
(6)Scala 如果期望是无返回值类型,可以省略等号
(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
二、函数高级
1:高阶函数
函数可以作为值进行传递
函数作为值进行传递的方法有两种实现方法,一个是指定参数和返回值类型,另一个是在函数后加空格和“_”
第一种:指定参数和返回值类型
def fun(num Int) : Int = {
println("fun方法被调用")
return num + 1
}
var f1 : Int => Int = fun
println(f1(12))
第二种:函数后加空格和“_”
def fun(num Int) : Int = {
println("fun方法被调用")
return num + 1
}
var f1 = fun _
println(f1(12))
函数作为参数进行传递
// (1) 定义一个函数, 函数参数还是一个函数签名; f 表示函数名称;(Int,Int)表示输入两个 Int 参数;Int 表示函数返回值
def f1(f: (Int, Int) => Int): Int = {
f(2, 4)
}
// (2)定义一个函数,参数和返回值类型和 f1 的输入参数一致
def add(a: Int, b: Int): Int = a + b
// (3)将 add 函数作为参数传递给 f1 函数,如果能够推断出来不是调用,_可以省略
println(f1(add))
println(f1(add _))
//可以传递匿名函数
}
函数可以作为函数返回值返回
def f1() = {
def f2() = {
}
f2 _
}
val f = f1()
// 因为 f1 函数的返回值依然为函数,所以可以变量 f 可以作为函数继续调用
f()
// 上面的代码可以简化为
f1()()
2:匿名函数
没有名字的函数就是匿名函数。
(x:Int)=>{
//函数体
}
x:表示输入参数类型;
Int:表示输入参数类型;
函数体:表示具体代码逻辑
传递匿名函数至简原则:
(1)参数的类型可以省略,会根据形参进行自动的推导
(2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。
(3)匿名函数如果只有一行,则大括号也可以省略
(4)如果参数只出现一次,则参数省略且后面参数可以用_代替
// (1)定义一个函数:参数包含数据和逻辑函数
def operation(arr: Array[Int], op: Int => Int) = {
for (elem <- arr) yield op(elem)
}
// (2)定义逻辑函数
def op(ele: Int): Int = {
ele + 1
}
// (3)标准函数调用
val arr = operation(Array(1, 2, 3, 4), op)
println(arr.mkString(","))
// (4)采用匿名函数
val arr1 = operation(Array(1, 2, 3, 4), (ele: Int) => {
ele + 1
})
println(arr1.mkString(","))
// (4.1)参数的类型可以省略,会根据形参进行自动的推导;
val arr2 = operation(Array(1, 2, 3, 4), (ele) => {
ele + 1
})
println(arr2.mkString(","))
// (4.2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情
况:没有参数和参数超过 1 的永远不能省略圆括号。
val arr3 = operation(Array(1, 2, 3, 4), ele => {
ele + 1
})
println(arr3.mkString(","))
// (4.3) 匿名函数如果只有一行,则大括号也可以省略
val arr4 = operation(Array(1, 2, 3, 4), ele => ele + 1)
println(arr4.mkString(","))
//(4.4)如果参数只出现一次,则参数省略且后面参数可以用_代替
val arr5 = operation(Array(1, 2, 3, 4), _ + 1)
println(arr5.mkString(","))
}
3:函数柯里化&闭包
闭包:函数式编程的标配
1)说明
闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
函数柯里化:把一个参数列表的多个参数,变成多个参数列表。
def create() = {
var arr : Array[Int] = Array(1,2,3,4,5)
def add(elem : Int) = {
//内部调用外部的变量
arr.+:(elem)
}
//获得add的值
add _
}
var f = create()
println(f(3).mkString(","))
println(create()(4).mkString(","))
4:递归
一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用
def test(i : Int) : Int = {
//跳出的逻辑
if (i == 1) {
1
} else {
//递归
i * test(I - 1)
}
}
5:控制抽象
值调用:把计算后的值传递过去
def f = ()=>{
println("f...")
10
}
foo(f())
}
def foo(a: Int):Unit = {
println(a)
println(a)
}
名调用:把代码传递过去
def main(args: Array[String]): Unit = {
def f = ()=>{
println("f...")
10
}
foo(f())
}
//def foo(a: Int):Unit = {
def foo(a: =>Int):Unit = {//注意这里变量 a 没有小括号了
println(a)
println(a)
}
6:惰性加载
当函数返回值被声明为 lazy 时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数。
lazy val res = sum(10, 30)
println("----------------")
println("res=" + res)
}
def sum(n1: Int, n2: Int): Int = {
println("sum 被执行。。。")
return n1 + n2
}
注意:lazy 不能修饰 var 类型的变量