总目录:https://blog.csdn.net/treesorshining/article/details/124697102
文章目录
1)面向对象编程
解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。
对象:用户
行为:登录、连接 JDBC、读取数据库
属性:用户名、密码
Scala 语言是一个完全面向对象编程语言,万物皆对象。
对象的本质:对数据和行为的一个封装
2)函数式编程
解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
例如:请求->用户名、密码->连接 JDBC->读取数据库
Scala 语言是一个完全函数式编程语言,万物皆函数。
函数的本质:函数可以当做一个值进行传递
3)在 Scala 中函数式编程和面向对象编程完美融合在一起了。
1.函数基础
1.1 函数基本语法
1)基本语法
2)案例
// 定义一个函数,实现打印传入的名称
// (1)函数定义
def f(arg: String): Unit = {
println(arg)
}
// (2)函数调用
// 函数名(参数)
f("hello world")
1.2 函数和方法的区别
1)核心概念
(1)为完成某一功能的程序语句的集合,称为函数。
(2)类中的函数称之方法。
注意:(1)Scala 语言可以在任何的语法结构中声明任何的语法
(2)函数没有重载和重写的概念;方法可以进行重载和重写
(3)Scala 中函数可以嵌套定义
2)案例
object Test_function {
def main(args: Array[String]): Unit = {
def test(arg: String): Unit = {
println(arg)
}
test("test") // test(注:如果上方test函数不存在,则会执行main函数外的那个test方法)
Test_function.test("test") // Test
}
def test(arg: String): Unit = {
println(arg.toUpperCase)
}
}
object Test_function {
// 方法可以进行重载和重写,程序可以执行
def main(): Unit = {
}
def main(args: Array[String]): Unit = {
// (1)Scala 语言可以在任何的语法结构中声明任何的语法
import java.util.Date
new Date()
// (2)函数没有重载和重写的概念,此处会报错
def test(): Unit ={
println("无参,无返回值")
}
test()
def test(name:String):Unit={
println()
}
//(3)Scala 中函数可以嵌套定义
def test2(): Unit ={
def test3(name:String):Unit={
println("test")
}
}
}
}
1.3 函数定义
1)函数定义
(1)函数 1:无参,无返回值
(2)函数 2:无参,有返回值
(3)函数 3:有参,无返回值
(4)函数 4:有参,有返回值
(5)函数 5:多参,无返回值
(6)函数 6:多参,有返回值
2)案例
// 函数 1:无参,无返回值
def test1(): Unit = {
println("无参,无返回值")
}
test1()
// 函数 2:无参,有返回值
def test2():String = {
return "无参,有返回值"
}
println(test2())
// 函数 3:有参,无返回值
def test3(s:String):Unit = {
println(s)
}
test3("test")
// 函数 4:有参,有返回值
def test4(s:String):String = {
return s+"有参,有返回值"
}
println(test4("test"))
// 函数 5:多参,无返回值
def test5(name:String, age:Int):Unit = {
println(s"$name, $age")
}
test5("test",40)
// 函数 6:多参,有返回值
def test5(name:String, age:Int):Unit = {
return s"$name, $age"
}
println(test6("test",40))
1.4 函数参数
1)案例
(1)可变参数
(2)如果参数列表中存在多个参数,那么可变参数一般放置在最后
(3)参数默认值,一般将有默认值的参数放置在参数列表的后面
(4)带名参数
// (1)可变参数
def test(s: String*): Unit = {
println(s)
}
// 有输入参数:输出 WrappedArray
test("Hello", "Scala") // WrappedArray(Hello, Scala)
// 无输入参数:输出 List()
test() // List()
// (2)如果参数列表中存在多个参数,那么可变参数一般放置在最后
def test2(name : String, s: String*): Unit = {
println(name + "," + s)
}
// 即使只传入一个参数,可变参数也有对应的值
test2("test1") // test1,WrappedArray()
test2("test1", "test2") // test1,WrappedArray(test2)
// (3)参数默认值
def test3(name : String, age : Int = 30): Unit = {
println(s"$name, $age")
}
// 如果参数传递了值,那么会覆盖默认值
test3("test", 20) // test, 20
// 如果参数有默认值,在调用的时候,可以省略这个参数
test3("test") // test, 30
// 一般情况下,将有默认值的参数放置在参数列表的后面
// Scala 函数中参数传递是,从左到右
// 类似于以下情况,将有默认值的参数放置在参数列表的前面则没有任何意义
// 因为必须要传入两个参数,默认值必然被覆盖,除非使用带名参数
def test4(sex : String = "男", name : String): Unit = {
println(s"$name, $sex")
}
//(4)带名参数
// 在存在默认值参数的情况下用的比较多
// 在有大量参数有默认值,但想覆盖其中一个或数个的情况下较为方便
test4(name="test1")
}
1.5 函数至简原则
函数至简原则:能省则省
1)至简原则细节
(1)return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
(2)如果函数体只有一行代码,可以省略花括号
(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
(4)如果有 return,则不能省略返回值类型,必须指定
(5)如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
(6)Scala 如果期望是无返回值类型,可以省略等号
(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
2)案例
// (0)函数标准写法
def f(s: String): String = {
return s
}
println(f("Hello"))
// 至简原则:能省则省
//(1) return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
def f1(s: String): String = {
s
}
println(f1("Hello"))
//(2)如果函数体只有一行代码,可以省略花括号
def f2(s: String):String = s
//(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
def f3(s: String) = s
println(f3("Hello"))
//(4)如果有 return,则不能省略返回值类型,必须指定。
def f4(): String = {
return "test"
}
println(f4())
//(5)如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
def f5(): Unit = {
return "test"
}
println(f5())
//(6)Scala 如果期望是无返回值类型,可以省略等号
// 将无返回值的函数称之为过程
def f6(s: String) {
println(s)
}
println(f6("test")) // test
// ()
// 注:在此处返回值为(),即空
//(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
def f7() = "test"
println(f7())
println(f7)
//(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
def f8 = "test"
// println(f8())
println(f8)
//(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
// 匿名函数
(x:String) => {
println(x) }
def f9 = (x: String) => {
println(x) }
def f10(f: String => Unit) = {
f("")
}
f10(f9)
println(f10((x: String) => {
println("test")}))
2.函数高级
2.1 高阶函数
1)函数可以作为值进行传递
def main(args: Array[String]): Unit = {
//(1)调用 foo 函数,把返回值给变量 f
//val f = foo()
val f = foo
println(f) // foo 相当于调用
// 1
//(2)在被调用函数 foo 后面加上 _,相当于把函数 foo 当成一个整体,传递给变量 f1
val f1 = foo _ // 在此处加了个下划线,即是赋值,如果输出不会进行调用,而会输出函数对象
foo() // foo
f1() // foo 注:如果输出f1,即是(包名.对象名$$$Lambda$5/1782704802@7cd62f43)
//(3)如果明确变量类型为函数类型,那么不使用下划线也可以将函数作为整体传递给变量
var f2:()=>Int = foo // 相当于foo _
}
def foo():Int = {
println("foo")
1
}
2)函数可以作为参数进行传递
将函数作为参数可以使同一个输入通过一个函数实现不同的操作,即只要作为参数的函数的类型满足,就可以通过此函数去完成不同的操作
例如:
func(f: (Int,Int) => Int) 函数
add(Int,Int) => Int实现相加功能
multiply(Int,Int) => Int实现相乘功能
add函数与multiply函数都满足func函数参数的要求,即func(add(Int,Int))与func(multiply(Int,Int))皆是正确的,但二者所实现的功能是不同的,则实现类似于func函数可以较为灵活地实现一个函数完成多种功能,而且传入符合条件的匿名函数也是可行的。
def main(args: Array[String]): Unit = {
// (1)定义一个函数,函数参数还是一个函数签名;f 表示函数名称;(Int,Int)表示输入两个 Int 参数;Int 表示函数返回值
// f: (Int, Int) => Int 即参数:(Int, Int) 返回值:Int的函数
def f1(f: (Int, Int) => Int, a: Int, b: Int): Int = {
f(a, b)
}
// (2)定义一个函数,参数和返回值类型和 f1 的输入参数一致
def add(a: Int, b: Int): Int = a + b
// (3)将 add 函数作为参数传递给 f1 函数,如果能够推断出来不是调用,_可以省略
println(f1(add, 3, 4)) // 7
println(f1(add _, 2, 4)) // 6
//可以传递匿名函数
}
3)函数可以作为函数返回值返回
def main(args: Array[String]): Unit = {
def f1() = {
def f2() = {
}
// 函数名 + _,表示函数本身,在此处表示返回f2函数本身
f2 _
}
val f = f1()
// 因为 f1 函数的返回值依然为函数,所以可以变量 f 可以作为函数继续调用
f()
// 上面的代码可以简化为
f1()()
}
2.2 匿名函数
1)说明
没有名字的函数就是匿名函数。
(x:Int)=>{函数体}
x:表示输入参数类型;Int:表示输入参数类型;函数体:表示具体代码逻辑
2)案例
需求 1:传递的函数有一个参数
传递匿名函数至简原则:
(1)参数的类型可以省略,会根据形参进行自动的推导
(2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。
(3)匿名函数如果只有一行,则大括号也可以省略
(4)如果参数只出现一次,则参数省略且后面参数可以用_代替
def main(args: Array[String]): Unit = {
// (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(",")) // 2,3,4,5
// (4)采用匿名函数
val arr1 = operation(Array(1, 2, 3, 4), (ele: Int) => {
ele + 1
})
println(arr1.mkString(",")) // 2,3,4,5
// (4.1)参数的类型可以省略,会根据形参进行自动的推导;
val arr2 = operation(Array(1, 2, 3, 4), (ele) => {
ele + 1
})
println(arr2.mkString(",")) // 2,3,4,5
// (4.2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。
val arr3 = operation(Array(1, 2, 3, 4), ele => {
ele + 1
})
println(arr3.mkString(",")) // 2,3,4,5
// (4.3) 匿名函数如果只有一行,则大括号也可以省略
val arr4 = operation(Array(1, 2, 3, 4), ele => ele + 1)
println(arr4.mkString(",")) // 2,3,4,5
//(4.4)如果参数只出现一次,则参数省略且后面参数可以用_代替
val arr5 = operation(Array(1, 2, 3, 4), _ + 1)
println(arr5.mkString(",")) // 2,3,4,5
}
def main(args: Array[String]): Unit = {
def dualFunctionOneAndTwo(fun: (Int, Int) => Int): Int = {
fun(1, 2)
}
val add = (a: Int, b: Int) => a + b
val minus = (a: Int, b: Int) => a - b
// 方式一:常见用法
println(dualFunctionOneAndTwo(add))
println(dualFunctionOneAndTwo(minus))
// 匿名函数简化
// 方式二:匿名函数直接填入
println(dualFunctionOneAndTwo((a: Int, b: Int) => a + b))
println(dualFunctionOneAndTwo((a: Int, b: Int) => a - b))
// 方式三:由于a,b的类型在定义时已经确定下来,故而可以省略
println(dualFunctionOneAndTwo((a, b) => a + b))
println(dualFunctionOneAndTwo((a, b) => a - b))
// 方式四:由于a,b都只出现过一次,故可以进行省略
println(dualFunctionOneAndTwo(_ + _))
println(dualFunctionOneAndTwo(_ - _))
// 若需计算b-a,则可以使用-a+b
println(dualFunctionOneAndTwo((a, b) => -a + b))
println(dualFunctionOneAndTwo(-_ + _))
}
2.3 高阶函数案例
模拟 Map 映射、Filter 过滤、Reduce 聚合
def main(args: Array[String]): Unit = {
// (1)map 映射
def map(arr: Array[Int], op: Int => Int) = {
for (elem <- arr) yield op(elem)
}
val arr = map(Array(1, 2, 3, 4), (x: Int) => {
x * x
})
println(arr.mkString(",")) // 1,4,9,16
// (2)filter 过滤。有参数,且参数再后面只使用一次,则参数省略且后面参数用_表示
def filter(arr:Array[Int],op:Int =>Boolean) = {
val arr1:ArrayBuffer[Int] = ArrayBuffer[Int]()
for(elem <- arr if op(elem)){
arr1.append(elem)
}
arr1.toArray
}
val arr1 = filter(Array(1, 2, 3, 4), _ % 2 == 1)
println(arr1.mkString(",")) // 1,3
// (3)reduce 聚合。有多个参数,且每个参数再后面只使用一次,则参数省略且后面参数用_表示,第 n 个_代表第 n 个参数
def reduce(arr: Array[Int], op: (Int, Int) => Int) = {
var init: Int = arr(0)
for (elem <- 1 until arr.length + 1) {
init = op(init, elem)
}
init
}
val arr2 = reduce(Array(1, 2, 3, 4), _ * _)
println(arr2) // 24
}
定义一个匿名函数,并将它作为值赋给变量 fun。函数有三个参数,类型分别为 Int,String,Char,返回值类型为 Boolean。要求调用函数 fun(0, “”, ‘0’)得到返回值为 false,其它情况均返回 true。
def main(args: Array[String]): Unit = {
val fun = (a: Int, b: String, c: Char) => if(a == 0 && b == "" && c == '0') false else true
println(fun(0, "", '0')) // false
println(fun(1, "", '0')) // true
}
定义一个函数 func,它接收一个 Int 类型的参数,返回一个函数(记作 f1)。它返回的函数 f1,接收一个 String 类型的参数,同样返回一个函数(记作 f2)。函数 f2 接收一个 Char 类型的参数,返回一个 Boolean 的值。要求调用函数 func(0) (“”) (‘0’)得到返回值为 false,其它情况均返回 true。
def main(args: Array[String]): Unit = {
def func(a: Int) = {
def f1(b: String) = {
def f2(c: Char) = if(a == 0 && b == "" && c == '0') false else true
f2 _
}
f1 _
}
println(func(0)("")('0')) // false
println(func(1)("")('0')) // true
}
// 简写方式一
def func1(a: Int) = {
(b: String) => {
(c: Char) => if(a == 0 && b == "" && c == '0') false else true
}
}
// 简写方式二
def func(a: Int): String => Char => Boolean = {
b => c => if(a == 0 && b == "" && c == '0') false else true
}
2.4 函数柯里化&闭包
闭包:函数式编程的标配
1)说明
闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
函数柯里化:把一个参数列表的多个参数,变成多个参数列表。
2)案例
def main(args: Array[String]): Unit = {
def add(a: Int, b: Int): Int = {
a + b
}
// 考虑固定一个加数的场景
def addByFour(b: Int): Int = {
4 + b
}
def addByFive(b: Int): Int = {
5 + b
}
// 将固定加数作为另一个参数传入,但是作为第一层参数传入
def addByFour_1(): Int=>Int = {
val a = 4
def addB(b: Int): Int = {
a + b
}
addB
}
def addByA(a: Int): Int=>Int = {
def addB(b: Int): Int = {
a + b
}
addB
}
println(addByA(1)(2)) // 3
// 新的场景,再加一个固定数时就无须固定,可灵活改变
// 在此处只是闭包最简单的例子,并非简单的a+b的问题,要深入原理,以此原理扩展,可以更加灵活地处理很多逻辑
val addByFour_2 = addByA(4)
val addByFive_2 = addByA(5)
println(addByFour_2(5)) // 9
// 简写
def addByA_2(a: Int): Int=>Int = {
(b: Int) => {
a + b
}
}
def addByA_3(a: Int): Int=>Int = {
b => a + b
}
def addByA_4(a: Int): Int=>Int = {
a + _
}
def addByA_5(a: Int): Int=>Int = a + _
// 柯里化
def addCurring(a: Int)(b: Int): Int = {
a + b
}
println(addCurring(1)(2)) // 3
}
2.5 递归
1)说明
一个函数/方法在函数/方法体内又调用了本身,称之为递归调用
-
方法调用自身
-
方法必须要有跳出的逻辑
-
方法调用自身时,传递的参数应该有规律
-
scala 中的递归必须声明函数返回值类型
2)案例
def main(args: Array[String]): Unit = {
println(fact(5))
}
// 递归实现阶乘
def fact(n: Int): Int = {
if(n == 0) 1 else fact(n - 1) * n
}
// 尾递归:纯函数式编程语言或是类似于scala这种用函数式编程特性的语言对递归的优化
// 在最后一行返回的只有对自身的调用,即不是类似于递归的压栈,而是要再次调用时将上次栈弹出再将此次压入,因此不会产生栈溢出的情况
def tailFact(n: Int): Int = {
// 将结果存储在一个变量中,则不需要再一直堆在栈中
// @tailrec保证此为一个正确的尾递归程序,不正确则会报错
@tailrec
def loop(n: Int, currRes: Int): Int = {
if(n == 0) currRes
loop(n - 1, currRes * n)
}
loop(n, 1)
}
2.6 控制抽象
1)值调用:把计算后的值传递过去
def main(args: Array[String]): Unit = {
def f(a: Int): Unit = {
println("a: " + a)
}
def f1(): Int = {
println("f1")
12
}
f(f1())
}
2)名调用:把代码传递过去
def main(args: Array[String]): Unit = {
def f1(): Int = {
println("f1")
12
}
// => Int 表示一段返回值为Int的代码块
// Int=>Int 表示参数为Int,返回值为Int的代码块
def f2(a: => Int): Unit = {
println("a: " + a)
println("a: " + a)
}
// 在此处f1执行了两次,由于参数是a,在f2中a出现了两次,故f1调用了两次
// 在此处并非将结果传入,而是将代码块完整传入
f2(f1()) // f1
// a: 12
// f1
// a: 12
// 直接放入代码块也可行
f2({
println("代码块")
12
})
}
案例
// 用闭包实现一个函数,将代码块作为参数传入,递归调用
// 实现类似while循环
def main(args: Array[String]): Unit = {
def myWhile(condition: =>Boolean): (=>Unit)=>Unit = {
def doLoop(op: =>Unit): Unit = {
if(condition) {
op
myWhile(condition)(op)
}
}
doLoop _
}
var n = 10
// 结果与使用while相同
myWhile(n >= 1) {
println(n)
n -= 1
}
// 简写
def myWhile_2(condition: =>Boolean)(op: => Unit): Unit = {
if(condition) {
op
myWhile_2(condition)(op)
}
}
n = 10
myWhile_2(n >= 1) {
println(n)
n -= 1
}
}
2.7 惰性加载
1)说明
当函数返回值被声明为 lazy 时,函数的执行将被推迟,直到首次对此取值,该函数才会执行。这种函数称之为惰性函数。
注意: lazy 不能修饰 var 类型的变量
2)案例
def main(args: Array[String]): Unit = {
lazy val result: Int = sum(13, 47)
println("1,函数调用")
println("2,result = " + result)
}
def sum(a: Int, b: Int): Int = {
println("3,sum调用")
a + b
}
/* 输出如下所示 */
// 1,函数调用
// 3,sum调用
// 2,result = 60