在Scala 中函数式编程和面向对象编程完美融合在一起了
1) 面向对象编程
解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。
对象:用户
行为:登录、连接 JDBC、读取数据库属性:用户名、密码
Scala 语言是一个完全面向对象编程语言。万物皆对象对象的本质:对数据和行为的一个封装
2) 函数式编程
解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
例如:请求->用户名、密码->连接 JDBC->读取数据库
Scala 语言是一个完全函数式编程语言。万物皆函数。函数的本质:函数可以当做一个值进行传递
// (1)函数定义
def f(arg: String): Unit = {
println(arg)
}
// (2)函数调用
// 函数名(参数)
f("hello world")
1)核心概念
2)案例实操
object TestFunction {
// 方法可以进行重载或重写,也可以执行
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(i:Unit):Unit = { // error
// println("test")
//}
// (3)Scala 中函数可以嵌套定义
def myFun():Unit={
println()
}
}
3.1 无参,无返回值
def test1(): Unit ={
println("无参,无返回值")
}
test1()
3.2 无参,有返回值
def test2():String={
return "无参,有返回值"
}
println(test2())
3.3 有参,无返回值
def test3(s:String):Unit={
println(s)
}
test3("haha")
3.4 有参,有返回值
def test4(s:String):String={
return s+"有参,有返回值"
}
println(test4("hello "))
3.5 多参,无返回值
def test5(name:String, age:Int):Unit={
println(s"$name, $age")
}
(1)可变参数
(2)如果参数列表中存在多个参数,那么可变参数一般放置在最后
(3)参数默认值,一般将有默认值的参数放置在参数列表的后面
(4)带名参数
4.1 可变参数
def test(s: String*): Unit = {
println(s)
}
test()
test("haha", "lala")
4.2 如果参数列表中存在多个参数,那么可变参数一般放置在最后
def test(s0: String, s: String*): Unit = {
println(s"${s0}有学生:${s}")
}
test("sfm", "zmy", "hlq", "zdc", "...")
4.3 参数默认值,一般将有默认值的参数放置在参数列表的后面
def test(name: String, age: Int = 23): Unit = {
println(s"${name}今年${age}岁")
}
test("chen chen")
// 如果参数传递了值,那么会覆盖默认值
test("chen chen", 18)
4.4 带名参数
test(name="辰辰")
函数至简原则:能省则省
5.0 函数标准写法
// (0)函数标准写法
def f(s: String): String = {
return s + " chen chen"
}
println(f("Hello"))
5.1 return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
def f1(s: String): String = {
s + " chen chen1"
}
println(f1("Hello"))
5.2 如果函数体只有一行代码,可以省略花括号
def f2(s: String): String = s + " chen chen1"
println(f2("Hello"))
5.3 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
def f3(s: String) = s + " chen chen1"
println(f3("Hello"))
5.4 如果有 return,则不能省略返回值类型,必须指定
def f4(s: String): String = {
return s + " chen chen"
}
println(f4("Hello"))
5.5 如果函数明确声明unit,那么即使函数体中使用 return 关键字也不起作用
def f5(s: String): Unit = {
return s + " chen chen"
}
println(f5("Hello"))
5.6 Scala 如果期望是无返回值类型,可以省略等号(将无返回值的函数称之为过程)
def f6(s: String) {
println(s + " chen chen")
}
f6("Hello")
5.7 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
def f7() = "明宇"
println(f7)
println(f7())
5.8 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
def f8 = "辰辰"
println(f8)
//println(f8()) // error
5.9 如果不关心名称,只关心逻辑处理,那么函数名、def 可以省略【匿名函数】
() => {
s"nihao"
}
// 调用
def f9 = (x: String) => {
s"nihao,${x}"
}
println(f9("ming yu"))
所谓的高阶函数,其实就是将函数当成一个类型来使用,而不是当成特定的语法结构。
package com.mingyu.spark.core.test
object Test_High_Fun {
def main(args: Array[String]): Unit = {
def f(name: String) = {
println(s"hello,${name}")
}
// 将函数f作为对象赋值给v1
// Function1 -> 有一个参数的函数类型
// [String,Unit] -> 参数类型为String,返回值类型为Unit
// 函数对象参数最多有22个,并不是函数的参数最多有22个
val v1: Function1[String, Unit] = f _
//println(v1.getClass.getSimpleName) // Test_High_Fun$$$Lambda$1/517938326
v1 //不会执行,只是一个对象
v1("mingyu")
// 以上方法太麻烦,使用以下方法
def test2(name: String, age: Int): Unit = {
println(s"${name}今年${age}岁了:)")
}
// v2的类型就是 (String,Int) => Unit
// 前面是参数类型,后面是返回值类型
val v2 = test2 _
v2("cc", 23)
}
}
package com.mingyu.spark.core.test
object Test_High_Fun {
def main(args: Array[String]): Unit = {
// 函数对象作为参数使用
def fun(): Unit = {
println("test...")
}
// 参数类型是函数对象
def test(f: () => Unit): Unit = {
f()
}
val f = fun _
test(f) // test...
}
}
package com.mingyu.spark.core.test
object Test_High_Fun {
def main(args: Array[String]): Unit = {
def outer(): () => Unit = {
def inner(): Unit = {
println("inner...")
}
inner _
}
outer()() // inner...
}
}
object Test {
def main(args: Array[String]): Unit = {
def fun( f:Int => Int ): Int = {
f(20)
}
println(fun((x:Int)=>{x * 10}))
println(fun((x)=>{x * 10}))
println(fun((x)=>x * 10))
println(fun(x=>x * 10))
println(fun(_ * 10))
}
}
闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
object Test_High_Fun {
def main(args: Array[String]): Unit = {
// addX 函数接受一个整数参数 x,并返回一个函数,
// 该函数接受一个整数参数 y,并返回 x + y 的结果。
// 然后,我们通过调用 addX(5) 来创建一个新函数 addFive,它将 5 作为 x 的值。
// 最后,我们调用 addFive(3),它返回 8,因为 5 + 3 = 8。
def addX(x: Int) = (y: Int) => x + y
val addFive = addX(5)
println(addFive(3)) // 8
}
}
函数柯里化:把一个参数列表的多个参数,变成多个参数列表。
package com.mingyu.spark.core.test
object Test_High_Fun {
def main(args: Array[String]): Unit = {
//def add(x: Int, y: Int) = x + y
//println(add(1, 2)) // 输出 3
// 现在,我们可以使用函数柯里化来将 add 函数转换为一系列只接受单个参数的函数:
def add(x: Int) = (y: Int) => x + y
val addOne = add(1)
println(addOne(2)) // 输出 3
"""
|在这个例子中,add 函数接受一个整数参数 x,并返回一个函数,
|该函数接受一个整数参数 y,并返回 x + y 的结果。
|然后,我们通过调用 add(1) 来创建一个新函数 addOne,它将 1 作为 x 的值。
|最后,我们调用 addOne(2),它返回 3,因为 1 + 2 = 3。
|
|通过函数柯里化,我们可以轻松地创建新的函数,例如 addTwo、addThree 等,
|它们分别将 2 和 3 作为 x 的值,并接受单个整数参数 y。
|这使得我们可以更容易地组合这些函数,并创建更复杂的函数,例如将它们作为参数传递给其他函数。
""".stripMargin
}
}
package com.mingyu.spark.core.test
object Test_High_Fun {
def main(args: Array[String]): Unit = {
def fibo(n: Int): Int = {
if (n > 2) return fibo(n - 1) + fibo(n - 2)
else if (n == 2) return 1
else return 0
}
//println(fibo(3))
for (elem <- 1 to 10) print(fibo(elem) + "\t")
}
}
// 0 1 1 2 3 5 8 13 21 34
package com.mingyu.spark.core.test
object Test_High_Fun {
def main(args: Array[String]): Unit = {
"""
|在Scala中,控制抽象是一种将控制流作为函数参数传递的技术。
|换句话说,控制抽象允许我们将一些代码块作为参数传递给函数,并在函数内部执行这些代码块。
|这使得我们可以编写更加灵活和可复用的代码。
|
|Scala中的控制抽象通常使用高阶函数和函数字面量实现。
|高阶函数是一个接受一个或多个函数作为参数的函数,而函数字面量是一个匿名函数,
|它可以在任何需要函数的地方使用。
|
|Scala中的控制抽象通常用于实现以下功能:
|
|延迟计算:控制抽象可以用于延迟计算,即只有在需要的时候才计算某个值。例如,lazy 关键字就是一种控制抽象,它允许我们延迟计算某个值,直到我们需要使用它。
|错误处理:控制抽象可以用于错误处理,例如使用 try 和 catch 块捕获异常。
|并发编程:控制抽象可以用于并发编程,例如使用 Future 和 Promise 实现异步计算
""".stripMargin
"""
|repeat 函数接受一个整数参数 n 和一个函数字面量 f,
|并在函数内部执行 f 函数 n 次。
""".stripMargin
def repeat(n: Int)(f: => Unit): Unit = {
for (i <- 1 to n) f
}
// 调用 repeat(5) 来创建一个新函数,并将一个打印语句作为 f 函数的参数
val f2 = repeat(5) {
println("hello")
}
// 用这个新函数,它将打印语句执行了 5 次
f2
}
}
(Scala语言真的好奇特)
package com.mingyu.spark.core.test
object Test_High_Fun {
def main(args: Array[String]): Unit = {
"""
|在这个例子中,变量 y 的值是立即计算出来的,因为它是一个非惰性值。
|而变量 z 的值需要计算 x 的值,因此在第一次访问 x 时,初始化代码块被执行,
|输出了 "Initializing x"。然后,变量 z 的值被计算为 43。
|
|惰性加载在 Scala 中还有许多其他的应用场景,例如:
|
|惰性集合:Scala 中的集合类支持惰性加载,可以在需要时才计算集合中的元素。
| 例如,Stream 就是一种惰性集合,它只会计算需要的元素,而不会一次性计算所有元素。
|惰性参数:Scala 中的函数可以定义惰性参数,即只有在需要时才会计算参数的值。
| 例如,可以使用 => 来定义惰性参数,例如 def f(x: => Int) = ...。
|
|此外,
| 在 Scala 中,惰性参数的值在被第一次访问后会被存放在一个临时变量中。
| 这个临时变量只会在第一次访问时被计算,然后将计算结果保存在这个变量中,
| 以后再次访问时直接返回这个变量的值,而不会重新计算。
|
|具体来说,当我们在函数体内访问一个惰性参数时,Scala 会自动将这个参数转换成一个函数,
|然后调用这个函数。这个函数只会在第一次调用时被计算,然后将计算结果保存在一个临时变量中。
|以后再次调用这个函数时,直接返回这个临时变量的值,而不会重新计算。
""".stripMargin
lazy val x = {
println("Initializing x")
42
}
val y = 1 + 2
println("y = " + y) // 输出 "y = 3"
"""
|第一次调用x,输出:
|Initializing x
|z = 43
""".stripMargin
val z = x + 1
println("z = " + z)
"""
|第二次调用x,输出
|u = 43
""".stripMargin
val u = x + 1
println("u = " + u)
}
}