Scala-函数式编程
函数式编程
// scala语言是函数式编程语言,所以,万物皆函数,所以方法其实就是函数
// def 函数名(参数1:参数类型1, 参数2:参数类型2):函数返回值类型 = {函数体}
def test1(): Unit = {
println("test function...")
}
函数与方法(区分)
函数()
java本质是将函数编译成一个新的方法
函数无法重载&重写
可嵌套使用
方法()
方法可重载&重写
// 函数作用域比较窄
// 方法的作用域比较大
函数的本质
编译后,函数会被编译为一个新的方法 private static final Unit test$1()
如果函数名与方法名相同,调用时默认为函数调用
参数与返回值
package test_funcation
object scala_testfuncation01 {
def main(args: Array[String]): Unit = {
def test() : Unit = {
println("test funcation")
}
def function_00(): Unit = {
println("无入参无返回值")
}
def function_01(): String ={
return ("无入参有返回值")
}
// 参数声明方式 -> 参数名 : 参数类型
def function_02(parameter:String): Unit = {
println("有参无返回值 :"+parameter)
}
def function_03(parameter1:String,parameter2:String): Unit = {
println(s"parameter1:${parameter1}\nparameter2:${parameter2}")
}
function_00()
function_00 // 无参时可省略小括号
println(function_01())
println(function_01)
function_02("参数")
function_03("参数1","参数2")
test()
scala_testfuncation01.test()
}
def test() : Unit = {
println("test method")
}
}
java中所有方法都是值传递 而非引用传递
函数的参数没有限制
可变参数
可变参数在使用时,都是集合对象
// 可变参数:参数类型加*
def test(names:String*) : Unit = {
for (name <- names){
println(name)
}
println(names)
}
// 可变参数应放在最后一个
def test2(age: Int,names: String*): Unit = {
for (name <- names) {
println(name)
}
println(names)
}
默认参数
// 设置参数默认值
// 底层为java封装的方法 当不传参数时 java自动调用该方法
def test3(password:String = "000000"): Unit = {
println(password)
}
def test4(password: String = "000000",name :String): Unit = {
println(name+password)
}
test()
test("张三")
test("张三","李四")
test("张三","李四","王五")
test3("1111111")
// 当多参输入时 可使用带名参数输入
test4(name = "张三")
test3()
test3()
// 调用时,参数如果想用使用默认值,可以不传递参数
// 调用时,如果不想使用默认值,注解传值即可
带名参数
// TODO 4. 带名参数:传递参数时,增加参数的名称,用于改变传参的顺序
def fun4( password:String = "000000", name:String ): Unit = {
}
// 参数在传递时默认为顺序匹配。
// scala可以通过特殊的语法改变传值的顺序
fun4(name="zhangsan")
可变参数和默认参数不能联合声明的
def test( password:String = "000000", name:String* ): Unit = {
}
// 可变参数和参数默认值是不能联合声明
// test()
test("000000")
test("000000", "zhangsan")
test("000000", "zhangsan", "lisi")
函数至简原则
所谓的至简原则,其实就是Scala的作者为了开发人员能够大幅度提高开发效率。通过编译器的动态判定功能,帮助我们将函数声明中能简化的地方全部都进行了简化。也就是说将函数声明中那些能省的地方全部都省掉。所以这里的至简原则,简单来说就是:能省则省。
def test1(): String = {
return "1"
}
println(test1())
// 如果只有一个返回值 可以省略return
def test2(): String = {
"2"
}
println(test2())
// 如果返回值类型可以确定 可以省略函数类型
def test3()={
"3"
}
println(test3())
def test3_2()={
val num = 20
if (num > 30) {
"大于30"
} else {
1
}
}
println(test3_2())
// 如果函数逻辑代码只有一条 可以省略花括号
def test4() = "4"
println(test4())
// 如果函数无参,省略小括号
def test5 = "5"
println(test5)
// TODO 过程函数 不会返回任何值
// 省略Unit ,同时不希望return起作用 可将等号同时省略
def test6{
"6"
}
println(test6)
// 关键字与函数名也可省略
// 省略的同时需将返回类型一同省略 并将=转换为 =>
// TODO 匿名函数
val f = () => {"null"}
println(f())
高阶函数编程
所谓的高阶函数,其实就是将函数当成一个类型来使用,而不是当成特定的语法结构。
函数也是个对象
def test1(): Unit = {
println("1")
}
val f1 = test1()
val f2 = test1 _ // 如果将函数作为一个整体赋值给变量 需要采用特殊符号:下划线 _
f2()
// 万物皆对象,但是对象都有类型,就意味着函数对象也有类型
// 函数独立使用时,参数声明没有个数限制的,但是如果将函数作为对象给别人使用,那么函数的参数声明的个数最多为22个
val f2 : Function0[Unit] = fun1 _
//val f22 : Function22[] = fun1 _
// 函数类型还有另外一种写法 : (输入参数类型)=>输出类型
val f3 : () => Unit = fun1 _
// 之所以使用下划线让函数作为对象使用,因为代码中没有明确变量的类型,所以需要通过取值类推断
// 如果变量声明的类型为函数类型,那么可以不使用下划线让函数作为对象
val ff : (String, Int) => String = test1
ff
// (String) => String
// (String, Int) => String
// String => String
将函数作为参数来使用
def test( f : (Int, Int) => Int ): Unit = {
val r = f(10, 20)
println(r)
}
//
// def fun2(x:Int, y:Int): Int = {
// x + y
// }
// def fun3(x:Int, y:Int): Int = {
// x - y
// }
// def fun4(x:Int, y:Int): Int = {
// x * y
// }
// def fun5(x:Int, y:Int): Int = {
// x + y
// }
//test(fun2) // 与test函数中的f命名无关 调用时只研究test与fun
//test(fun3)
//test(fun4)
// TODO 匿名函数主要应用于函数作为参数使用
// test(
// (x:Int, y:Int) => {
// x - y
// }
// )
// TODO 匿名函数在使用时也可以存在至简原则
// 1. 如果函数体的逻辑代码只有一行,大括号可以省略,代码写在一行中
// test(
// (x:Int, y:Int) => x - y
// )
// 2. 如果参数的类型可以推断出来,那么参数类型可以省略的
// test(
// (x, y) => x - y
// )
// 3. 如果参数只有一个的话,参数列表的小括号可以省略
// 4. 如果参数在使用时,按照顺序只使用了一次,那么可以使用下划线代替参数,
//test(_ * _)
def test( f : (String)=>Unit ): Unit = {
f("zhangsan")
}
def fun( name:String ): Unit = {
println(name)
}
test(fun)
// test(
// ( name:String ) => {
// println(name)
// }
// )
// test(
// ( name:String ) => println(name)
// )
// test(
// ( name ) => println(name)
// )
// test(
// name => println(name)
// )
test(println(_)) // 匿名函数的至简原则的最终版
//test(println)
//test(fun)
def test( f : (Int, Int)=>Int ) = {
f(10, 20)
}
println(
test(
(x, y) => 2 * x + y
)
)
println(test(2*_ + _)) //运算符也是方法 所以也可以至简原则
def test( x :Int, y:Int, f : (Int, Int)=>Int ): Unit = {
val r = f(x, y)
println(r)
}
//test(10, 20, _+_)
test(10, 20,
(x:Int, y:Int) => {
x + y
}
)
将函数作为返回值返回
一般应用于将内部的函数在外部使用
def test(): Unit = {
println("function...")
}
def fun() = {
test _
}
//val f = fun _
//val ff = f()
//ff()
fun()()
test()
def outer() = {
def mid() = {
def inner(): Unit = {
println("inner...")
}
inner _
}
mid _
}
outer()()()
// TODO 闭包
// 一个函数使用了外部的变量,把这个变量包含到了它的内部来使用,改变了这个变量的生命周期
// 将当前的代码形成了一个闭合的环境,这个环境称之为闭包环境,简称为闭包
// Scala2.12版本前闭包功能采用的是匿名函数类型实现
// Scala2.12版本闭包功能采用的是更改函数声明实现
def outer( a : Int ) = {
def inner( b : Int ): Int = {
a + b
}
inner _
}
//println(outer(10)(20)) 与下面功能相同
val f = outer(10)
val ff = f(20)
println(ff)
控制抽象---暂不了解
// TODO : 控制抽象
// 抽象
// 函数类型:() =>Unit
def myWhile(op: => Boolean) = {
op
}
// 参数类型不完整,那么在传递参数时,也是不完整:只有传递代码就可以,不需要完整的声明
// 可以采用控制抽象设计语法
Breaks.breakable {
for ( i <- 1 to 5 ) {
Breaks.break()
}
}
柯里化(Curry)
// 将无关的参数分离开
def test( a:Int, b:Int ): Unit = {
for ( i <- 1 to a ) { // 10min
println(i)
}
for ( j <- 1 to b ) { // 20min
println(j)
}
}
def test1(a:Int)(b:Int): Unit = {
for ( i <- 1 to a ) { // 10min
println(i)
}
for ( j <- 1 to b ) { // 20min
println(j)
}
}
val a = 10 // 10min
val b = 20 // 20min
test(a, b) // 60min
test1(a)(b)
递归
StackOverflowError
多次进栈,导致栈溢出
// 1. scala中要求递归函数必须明确声明返回值类型
// 2. 函数内部调用自身
// 3. 一定要有跳出递归的逻辑
// 4. 递归函数在调用时传递的参数之间有关系
// 阶乘
// 5!
// StackOverflowError
// Overflow : 滚动
// def myRecursion(num:Int):Int = {
// if ( num <= 1 ) {
// 1
// } else {
// num * myRecursion(num-1)
// }
// }
//
// println(myRecursion(5))
def myRecursion(num:Long):Long = {
if ( num <= 1 ) {
1
} else {
num + myRecursion(num-1)
}
}
println(myRecursion(10000))
尾递归
当前一个进栈后运行才调用下个进栈,不会导致栈溢出
def test(): Unit = {
test()
println("test")
}
//test()
// Scala中尾递归不是真正的递归,是由编译器进行了优化,形成了while循环。所以应该称之为伪递归
// java中也有尾递归。但是不会优化为while循环。
// 尾递归也会出现
// 伪递归
// 尾递归
def test1(): Unit = {
println("test")
test1()
}
test1()
惰性函数
// 函数结果没使用,那么这个函数就不会执行
def fun9(): String = {
println("function...")
"zhangsan"
}
lazy val a = fun9() // Load 10000User //lazy 关键字
println("----------") // 2Min
println(a) // 10000 User