函数式编程
函数基础
一切皆函数(真),一切皆对象(真)
【PS:而不像Java 还有为了舔C++遗留的基本数据类型】
//基础语法:
def sum (x: Int , y: Int) : Int = { x + y }
关键字 函数签名 函数返回值类型 函数体
//函数 || 方法:
定义在 class 类体中的 函数 称为 方法【当然二者结构都可是一样的】{
定义在 方法 中的 函数 称为 函数
注1:Scala语言可以在任何的语法结构中声明任何的语法
注2:函数没有重载和重写的概念;方法可以进行重载和重写
注3:Scala中函数可以嵌套定义
函数嵌套
object TestFunction {
def main(args: Array[String]): Unit = {
//嵌套 定义函数
def test1() ={
def test2(name:String):Unit={
println("函数可以嵌套定义")
}
test2("abc")
}
test1() // 函数可以嵌套定义
}
}
函数参数
函数参数:
(1)可变参数
(2)如果参数列表中存在多个参数,那么可变参数放置在最后
(3)参数默认值,一般将有默认值的参数放置在参数列表的后面
(4)带名参数,如果有默认值参数不在参数列表的最后,可以在调用函数的时候使用 命名参数
//1.可变参数 Type*
def test( s : String* ): Unit = {
println(s)
}
//2.多参数,可变参数放置最后
def test2( id: Int, s: String* ): Unit = {
println(s)
}
//3.参数默认值
def test3( name : String, age : Int = 30 ): Unit = {
println(s"$name, $age")
}
def test4( age : Int = 30, name : String ): Unit = {
println(s"$name, $age")
}
//(4)带名参数
test4(name="ximenqing") //ximengqing,30
函数至简原则
(1)return 可以省略,Scala会使用函数体的最后一行代码作为返回值
(2){} 花括号可以省略,如果函数体只有一行代码,
(3): Type 参数类型/函数返回值类型 如果能够推断出来那么可以省略
(4)= 等号可以省略,如果函数期望是无返回值 Unit类型,
(5)() 小括号可以省略,如果函数没有显式声明参数列表(),那么调用时小括号必须省略
(6)def + 函数名 可以省略,如果不关心名称,只关心函数内部的逻辑处理
(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
(8)如果函数体有return关键字,则不能省略声明函数返回值类型,必须指定
(9)如果函数返回值显式声明 Unit类型,那么即使函数体中使用return关键字也不起作用
函数高级
关于,Scala 函数Function 与方法Method 的区别:
如果定义在类体中,使用def 关键字,我们通常叫它为 方法
如果定义在方法中,通常不使用 def 关键字,我们通常加它为 函数
高阶函数
函数作为值传递 val a = foo()
函数作为整体传递 val b = foo _
函数作为参数传递 (f: U => V)
函数作为函数返回值 { f2_ }
一切皆函数
object TestFuncHigh {
def main(args:Array[String]): Unit ={
//1.将函数返回值 传递给 var/val
println("--- 函数返回值被传递 ---")
val fv = fValue(1) // 简写==> val fv = fValue()
println(fv)
//2.1将函数整体 传递给 var/val _代表参数/返回值类型推断
println("--- 函数整体被传递 ---")
val ff = fValue _
println(ff(2))
//2.2明确var/val 函数参数&返回值类型,则可以不使用 _
var ff2:(Int)=>Int = fValue
println(ff2(3))
//3.将嵌套定义的内层 函数整体 作为外层 函数返回值 传递给变量(函数整体传递的一种)
println("--- 内层函数整体作为外层函数返回值被传递 ---")
println("单层调用")
val innerF = f1()
println("单层调用")
innerF()
println("双层调用")
f1()()
//4.函数作为形式参数被定义 及 传递调用
println("--- 函数作为形式参数被定义及传递调用 ---")
val fP = fParam(fValue)
println(fP)
}
def fValue(a: Int): Int ={
println("fValue")
a
}
def f1 ()={
println("f1")
def f2 ()={
println("f2")
}
f2 _
}
def m1(a:Int) = {
def m2(b:Int)= {
def m3(m4:(Int,Int)=>Int)= {
m4(a,b)
}
m3 _
}
m2 _
}
//完整函数定义:def 函数签名 : 函数返回值 = {函数体}
//函数作为形式参数声明:参数列表 (形参函数名: 参数1,参数2,... => 形参函数返回值类型)
//注:形参函数写在 参数列表 == 小括号()里,也就没办法写完整函数定义,一切从简
def fParam(f: Int => Int): Int = {
//调用传入参数:函数f
f(4)
}
}
匿名函数
匿名函数 == 不关心函数名称 / 不二次调用的函数。如 形参函数
格式:(参数列表)=>{函数体}
传递匿名函数简化原则:
(1)参数类型 :Type 省略,会根据形参进行自动的推导
(2)只有一个参数,则圆括号 () 可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。
(3)函数体只有一行,则大括号 {} 也可以省略
(4)参数只出现一次,则参数省略且后面参数调用可用"_"代替
object TestAnonymity {
def main(args: Array[String]): Unit = {
//0.传入 实参 函数f1
val arr0 = f0(Array(1, 2, 3, 4), f1)
println(arr0.mkString(","))
//1.传入 一个实参 匿名函数
val arr1 = f0(Array(1, 2, 3, 4), _ + 1)
println(arr1.mkString(","))
/**
* val arr1 = f0(Array(1,2,3,4),(ele:Int)=>{ele+1})
* val arr2 = f0(Array(1,2,3,4),(ele)=>{ele+1})
* val arr3 = f0(Array(1,2,3,4),ele=>{ele+1})
* val arr4 = f0(Array(1,2,3,4),ele=>ele+1)
* val arr5 = f0(Array(1,2,3,4),_+1)
*/
//2.传入 两个实参 匿名函数
val arr2 = f2(Array(1, 2, 3, 4), _ + _)
println(arr2.mkString(","))
}
def f0(arr: Array[Int], op: Int => Int) = {
for (elem <- arr) yield op(elem)
}
def f2(arr: Array[Int], op: (Int, Int) => Int) = {
for (elem <- arr) yield op(elem, 1)
}
def f1(m1: Int): Int = {
m1 + 1
}
}
闭包&函数柯里化
闭包 == {外部函数的局部变量(外部函数已弹栈的情况下)存活 + 内部函数} ==> 新的对象 == 同一生命周期
函数柯里化 == 一个参数列表 转换为 多个参数列表 && 嵌套函数(闭包)的声明简化(可配合代码块传递)
object TestBBFunction {
def main(args: Array[String]): Unit = {
var a = 10
//非函数式嵌套调用:
//特点:在最内层调用未完成时,外层调用的方法并不弹栈
// 以达到外层局部变量共享给内部调用函数,当外部调用的方法弹栈后,其局部变量也弹栈/释放
TraditionM1(a)
//函数式嵌套调用
//特点:外层方法执行完毕后,方法出栈,而其仍被内层函数使用的局部变量不弹栈
// 与内层函数组成一个共生命周期的环境/共同体/新对象【形成=>官方·闭包】
val b = f1(a)()
//多次执行,只执行()()()第三层嵌套函数,其所需的第一层方法的局部变量仍然留存。
b() //10
b() //10
//柯里化,简化闭包函数(嵌套函数)调用
klh(1)("abc")
}
/**
* 传统方法的链式调用,缺点:大量嵌套方法压栈,栈空间不足
* 优点:层次分明,易于理解
* @param b 共享给m2,m3的参数
*/
def TraditionM1(b: Int): Unit = {
m2(b)
}
def m2(c: Int): Unit = {
m3(c)
}
def m3(d: Int): Unit = {
println(d)
}
/**
* 1.嵌套的内部方法,可共享可能已被方法栈弹出的外部方法的局部变量 ==> 闭包
* 2.定义完f2,f3,需要将这些方法作为方法返回值返回
* 3.方法的嵌套调用,优点:执行完的方法直接弹栈,但其嵌套范围内仍被引用的局部变量不会弹栈,
* 也就是:解决了栈空间被嵌套方法占用问题,且弹栈方法的局部变量的生命周期被延长同内部嵌套函数
* 也就是:嵌套外层的局部变量与其内部嵌套函数形成一个闭合共存空间 = 对象
* @param b
*/
def f1(b: Int)= {
def f2()= {
def f3()= {
println(b)
}
f3 _
}
f2 _
}
/**
* 柯里化简化多层方法嵌套
* 注:这样写,会将第一层方法逻辑省略,但仍可通过——传递代码块的方式
* 在最内层方法中加入外层函数逻辑
*
* def myWhile(condition: => Boolean)(op: => Unit): Unit = {
* //1.如果满足循环条件,执行循环体op
* if (condition) {
* op
* myWhile(condition)(op)
* }
* }
*
* @param a 第一层方法
* @param s 第二层方法
*/
def klh(a:Int)(s:String): Unit ={
println(a)
}
}
控制抽象
函数参数传递:3种情况
1.值传递
2.函数传递
3.代码块传递 ==> 控制抽象
object TestWhileFunction {
def main(args: Array[String]): Unit = {
//0.定义一个匿名函数,作为参数化函数
val block = (res: Int) => {
println("--- block ---")
res
}
//1.传递值(block函数返回值)
foo(block(10))
/*
--- block ---
10
10
*/
//2.传递代码块
foo2(block(10))
/*
--- block ---
10
--- block ---
10
*/
//3.传递函数
foo3(block)
//4.控制抽象实例:myWhile 函数,传入两个参数
var i = 1
myWhile(i <= 10) {
println(i)
i += 1
}
}
//传递值
//格式:(参数名: 参数类型)
def foo(a: Int): Unit = {
println(a)
println(a)
}
//传递代码块
//格式:(参数化代码块名: => 代码块返回值类型)
def foo2(a: => Int) {
println(a)
println(a)
}
//传递函数
//格式:(参数化函数名: 参数化函数参数类型 => 参数化函数返回值类型)
def foo3(f: Int => Int): Unit = {
println(f(10))
println(f(10))
}
/**
* 注意:condition 不能为变量参数(值传递),而应是代码块参数(可动态变化)
*
* @param condition 条件代码块
* @param op 执行代码块
*/
def myWhile(condition: => Boolean)(op: => Unit) {
//1.如果满足循环条件,执行循环体op
if (condition) {
op
myWhile(condition)(op)
}
}
}
递归
object TestDGFunction {
def main(args: Array[String]): Unit = {
println(jc(5))
println(addFunction(20, 2))
}
/**
* 阶乘方法
* @param n n的阶乘
* @return 递归式
*/
def jc(n:Int):Int={
//1.收敛条件:注意使用return结束整个函数
if( n == 1)
return 1
//2.处理
//3.return 递归
n*jc(n-1)
}
/**
* 位运算,实现的加法器
* @param a 加数1 / 无进位和数
* @param b 加数2 / 进位
* @return 迭代式
*/
def addFunction(a:Int,b:Int): Int ={
if (b==0)
return a
addFunction(a^b,(a&b)<<1)
}
}
惰性执行
object TestLazyFunction {
def main(args: Array[String]): Unit = {
//惰性关键字:只被允许定义在 val 不变量前
//当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行
lazy val res = sum(10,10)
println("_______")
println("res="+res)
}
def sum (n1:Int,n2:Int):Int = {
println("--sum--")
n1 + n2
}
}