目录
接上篇:(一)Scala语言——入门
一、函数式编程
1)面向对象编程
解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。
对象:用户;
行为:登录、连接jdbc、读取数据库。
属性:用户名、密码。
Scala语言是一个完全面向对象编程语言。万物皆对象。
2)函数式编程
解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
例如:请求->用户名、密码->连接jdbc->读取数据库
Scala语言是一个完全函数式编程语言。万物皆函数(万物皆函数不太对)
函数可以当做一个值进行传递,高阶函数。
3)在Scala中函数式编程和面向对象完美编程融合在一起了。
4)函数(算子),方法:
其实是一个东西,只是场景不一样,叫法不一样,在函数式编程习惯叫函数。
1. 函数基本语法
函数的签名:
def add(a:Int,b:Int):Int
三要素:函数名 形参列表 返回值类型
- 基本语法
- 实操
package com.fseast.scala002.fun
object TestFunction {
def main(args: Array[String]): Unit = {
//2. 函数调用
f("Hello World")
}
//1. 函数声明
def f(s:String): Unit ={
print(s)
}
}
名词解释
一、纯函数: 特点:
- 不产生副作用。 常见的副作用:打印到控制台,修改了外部变量的值,向磁盘写入文件,…
- 引用透明, 函数的返回值,只和形参有关,和其他任何的值没有关系 纯函数的好处:天然的支持高并发,
二、只有副作用,没有返回值,叫过程。
细节:
-
函数体内可以没有return,这个时候会自动的把最后一行代码的值返回。
-
返回值类型也可以省略(:和返回值类型一起省),scala 会根据最后一行代码的值进行推导返回值类型。
-
如果有return, 则不能省略返回值类型,必须指定
-
= 省略的话,表示这个函数一定返回的是 Unit, 不管函数体怎么定义,一定返回Unit。
返回Unit 的函数, 有时候称为过程。 -
如果函数的形参格式是0,则调用的时候,可以省略()
-
如果没有形参,则声明的时候也可以省略(),调用的就不能再有圆括号,和val的声明要区别。
-
可变参数
-
参数的默认值
当传递时不传,则会使用默认值。 -
命名参数:
调用的时候,指定形参的名字。
2. 函数和方法的区别
个人理解是方法和函数其实是一个东西,只是场景不一样,叫法不一样,在函数式编程习惯叫函数。
(1)Scala语言的语法非常灵活,可以在任何的语法结构中声明任何的语法
(2)函数没有重载和重写的概念;方法可以进行重载和重写
(3)scala中函数可以嵌套定义
3. 函数声明和函数参数
- 函数的声明
(1)函数1:无参,无返回值
(2)函数2:无参,有返回值
(3)函数3:有参,无返回值
(4)函数4:有参,有返回值
(5)函数5:多参,无返回值 - 实例实操
(1)可变参数
(2)如果参数列表中存在多个参数,那么可变参数一般放置在最后
(3)参数默认值
(4)带名参数
package com.fseast.scala002.fun
object TestFunction2 {
def main(args: Array[String]): Unit = {
//1. 测试可变参数
// test1("11test","jjj")
//2. 测试可变参数2,
//test2("t11","t22")
//3. 测试参数默认值,带名参数
test3("fsd",t = 22)
}
//1. 可变参数
def test1(s:String*)={
println(s)
}
//2. 如果参数列表中存在多个参数,那么可变参数一般放置在最后
def test2(name:String,s:String*)={
println(name + "," + s)
}
//3. 参数默认值
def test3(name:String,age:Int = 30,t:Int) ={
println(name,age)
}
}
4. 函数至简原则
函数至简原则:能省则省
1)至简原则细节
(1)return可以省略,Scala会使用函数体的最后一行代码作为返回值
(2)返回值类型如果能够推断出来,那么可以省略
(3)如果函数体只有一行代码,可以省略花括号
(4)如果函数无参,则可以省略小括号。若定义函数时省略小括号,则调用该函数时,也需省略小括号;若定时函数时未省略,则调用时,可省可不省。
(5)如果函数明确声明Unit,那么即使函数体中使用return关键字也不起作用
(6)Scala如果想要自动推断无返回值,可以省略等号
(7)如果不关心名称,只关系逻辑处理,那么函数名(def)可以省略
(8)如果函数明确使用return关键字,那么函数返回就不能使用自行推断了,需要声明返回值类型
2)案例实操
// 0)函数标准写法
def f1( s : String ): String = {
return s + " jinlian"
}
println(f1("Hello"))
// 至简原则:能省则省
//(1) return可以省略,scala会使用函数体的最后一行代码作为返回值
def f2( s : String ): String = {
s + " jinlian"
}
println(f2("Hello"))
// 如果函数名使用return关键字,那么函数就不能使用自行推断了,需要声明返回值类型
/*
def f22(s:String)={
return "jinlian"
}
*/
//(2)返回值类型如果能够推断出来,那么可以省略
def f3( s : String ) = {
s + " jinlian"
}
println(f3("Hello"))
//(3)如果函数体只有一行代码,可以省略花括号
//def f4(s:String) = s + " jinlian"
//def f4(s:String) = "jinlian"
def f4() = " dalang"
// 如果函数无参,但是声明参数列表,那么调用时,小括号,可加可不加。
println(f4())
println(f4)
//(4)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
def f5 = "dalang"
// val f5 = "dalang"
println(f5)
//(5)如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
def f6(): Unit = {
//return "abc"
"dalang"
}
println(f6())
//(6)scala如果想要自动推断无返回值,可以省略等号
// 将无返回值的函数称之为过程
def f7() {
"dalang"
}
println(f7())
//(7)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
//()=>{println("xxxxx")}
val f = (x:String)=>{"wusong"}
// 万物皆函数 : 变量也可以是函数
println(f("ximenqing"))
//(8)如果函数明确使用return关键字,那么函数返回就不能使用自行推断了,需要声明返回值类型
def f8() :String = {
return "ximenqing"
}
println(f8())
5. 高阶函数
1)说明:
一个函数,可以接收一个函数作为参数,这样的函数就叫高阶函数(高阶算子)。
对一个函数做什么?
(1)定义函数
(2)调用函数
(3)传递函数
2)案例实操1
object HighDemo1 {
def main(args: Array[String]): Unit = {
//把函数 add 当做参数传到 f1 函数中
println(f1(add))
}
def add(i1:Int,i2:Int)={
i1 + i2
}
def f1(f:(Int,Int)=>Int)={
f(3,5)
}
}
案例实操2:高阶函数常用的场景
object HighDemo2 {
def main(args: Array[String]): Unit = {
operation(Array(1,2,3,4,5),op)
}
def op(i: Int)={
println(i + i)
}
def operation(arr:Array[Int],op:Int=>Unit)={
for (i <- arr) {
//遍历第一个参数(数组)的内容,当做参数传到第二次参数(函数)中
op(i)
}
}
}
6. 匿名函数
1)需求:
通过上面高阶函数的案例2中可以发现,每次如果要把一个函数当做参数传到另一个函数时,要写一个函数,而且该函数有可能只用一次,不够优雅。
2)说明:
没有名字的函数就是匿名函数,可以直接通过函数字面量(λ表达式)来设置匿名函数,函数字面量定义格式如下。
3)案例实操
object HighDemo2 {
def main(args: Array[String]): Unit = {
//版本1:原版匿名函数
operation(Array(1,2,3,4,5),(ele:Int) =>{
println(ele+ele)
})
/**省略版本:
* 1.参数的类型可以省略,
* 2.类型省略之后,只有一个参数,则圆括号可以省略,
* 其他的情况:没有参数和参数超过1 永远不能省圆括号
* 3. 匿名函数如果只有一行,则大括号也可以省略。
*/
//版本2:省略了参数类型和圆括号(只有一个参数所以圆括号可以省)
operation(Array(1,2),ele=>{
println(ele + 1)
})
//版本3:只有一行 大括号可以省略
operation(Array(1,2),ele => println(ele + 1))
//版本4:只有一行,且格式简单,用占位符替代:形参只使用一次
operation(Array(1,2), _+1)
}
def operation(arr:Array[Int],op:Int=>Unit)={
for (i <- arr) {
//遍历第一个参数(数组)的内容,当做参数传到第二次参数(函数)中
op(i)
}
}
}
6.1 高阶函数和匿名函数的使用案例
使用案例一:
object HighDemo4 {
def main(args: Array[String]): Unit = {
//把函数体x*x 传入到map中,
var arr = map(Array(1,2,3,4),x => x * x)
println(arr.mkString(","))
}
def map(arr:Array[Int],op: Int => Int)={
for (i <- arr) yield op(i)
}
}
使用案例二:
拦截
object HighDemo5 {
def main(args: Array[String]): Unit = {
//2. 测试拦截
//下面这行代码可以省略为:val arr1 = filter(Array(1,2,3,4,5),_%2==1)
val arr1 = filter(Array(1,2,3,4,5),x=>x%2==1)
println(arr1.mkString(","))
}
//2. 拦截,
def filter(arr: Array[Int], op: Int => Boolean): Array[Int] = {
for (i <- arr if op(i)) yield i
}
}
使用案例三:
object HighDemo4 {
def main(args: Array[String]): Unit = {
//3. 测试模拟reduce
//下面这行代码可以省略为var red = reduce(Array(1,2,3,4),_+_)
var red = reduce(Array(1,2,3,4),(x,y) => x + y)
println(red)
}
//3. 模拟reduce
def reduce(arr1: Array[Int],op1: (Int,Int) => Int)={
var init = arr1(0)
for (e1 <- 1 until arr1.length) {
init = op1(init,arr1(e1))
}
init
}
}
7. 闭包和函数柯里化
闭包:
函数式编程的标配
如果一个函数,访问到了它的外部(局部)变量的值。那么这个函数和他所处的环境,称为闭包
函数的柯里化:把一个参数列表的多个参数,变成多个参数列表。
object ClosureDemo1 {
def main(args: Array[String]): Unit = {
//计算4+5的值
println(add(4)(5))
//也可以这样
val add4 : Int => Int = add(4)
println(add4(5))
//测试函数的柯里化
println(add1(2)(8))
}
def add(a: Int) = {
//a是外部变量,闭包
(b: Int) => a + b
}
//函数柯里化
def add1(a:Int)(b:Int)=a+b
}
8. 递归调用
自己调自己
一个函数/方法在函数/方法体内又调用了本身,称之为递归调用
object RecusiveDemo {
def main(args: Array[String]): Unit = {
println(jieceng(10))
}
//计算阶层
//递归调用的返回值类型不可以推
def jieceng(n:Long):Long={
if (n == 1) 1
else n * jieceng(n-1)
}
}
9. 控制抽象
值调用:把值传过去,就第一次计算。
名调用:把一段代码(表达式)传过去,用一次执行一次这段代码,
值调用和名调用:
object ControlAbs {
def main(args: Array[String]): Unit = {
//1. 值调用
foo1(3+4)
//2. 名调用
foo2(3+5)
def f()={
println("--------")
12
}
//值调用
foo1(f())
//名调用
foo2(f())
//
foo3(() =>{
13
})
}
//1. 值调用
def foo1(a: Int)={
println(a)
println(a)
println(a)
}
//2. 名调用:传一个代码块
def foo2(a: => Int)={
println(a)
println(a)
println(a)
}
//3. 与2.名调用功能相似,传一个无参的函数,结果是<function0>
def foo3(a: () => Int)={
println(a)
println(a)
println(a)
}
}
控制抽象:
object ControlAbs3 {
def main(args: Array[String]): Unit = {
var i = 1
myWhile(i<=100){
println(i)
i += 1
}
}
def myWhile(c: => Boolean)(op: => Unit):Unit ={
if (c){
op
myWhile(c)(op)
}
}
}
10. 惰性函数
1)说明
当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数称之为惰性函数。
2)案例实操
def main(args: Array[String]): Unit = {
lazy val res = sum(10, 30)
println("----------------")
println("res=" + res)
}
def sum(n1: Int, n2: Int): Int = {
println("sum被执行。。。")
return n1 + n2
}
输出结果:
----------------
sum被执行。。。
res=40
注意:lazy 不能修饰 var 类型的变量
val a = {10} //立即求出来结果
lazy val b //第一次使用求值
def c = 30 //用一次求一次