• 面向对象编程

解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题

  • 函数式编程

解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数)。

严格的函数式语言是没有变量的赋值行为,讲究的是引用透明性,也就是说一个表达式返回一个值,那么它永远返回一个值,不会变。 函数式语言常常和递归联系起来,这是因为一般的循环结构,除非 while(1) 这种死循环,都是和表达式的变动关联起来的,比如说 while(n) 就是要不断修改 n 的值直到 n == 0,在函数式语言中是必须避免的。 递归通过调用函数的参数不同,来达到数据的变动却不破坏引用透明性。并且如果加入尾递归优化,那么递归的性能和循环是等价的。 函数式语言另外一个特点,也是一个语言能自称函数式的关键所在是,函数是“一等公民”,这是说能像操作数据一样在函数中动态生成新的函数,可以将函数赋值给变量,可以把函数放到数据结构里,可以把函数作为参数和返回值。 达到这一个特性的语言都可以宽泛的称作函数式编程语言,并不和过程式和面向对象冲突。

Scala是一门面向对象的语言,也是函数式语言
**

函数基本语法

image.png

函数和方法的区别

1) 核心概念
(1)为完成某一功能的程序语句的集合,称为函数。
(2)类中的函数称之方法。
2) 案例实操
(1 ) Scala语言可以在任何的语法结构中声明任何的语法
(2)函数没有重载和重写的概念;方法可以进行重载和重写
(3 ) Scala中函数可以嵌套定义

函数定义

(1) 函数1:无参,无返回值
(2) 函数2:无参,有返回值
(3) 函数3:有参,无返回值
(4) 函数4:有参,有返回值
(5)函数5:多参,无返回值
(6)函数6:多参,有返回值

  1. def f1(): Unit={
  2. println("无参无返回值")
  3. }
  4. def f2():Int={
  5. println("无参,有返回值")
  6. return 12
  7. }
  8. def f3(name: String):Unit={
  9. println("有参,无返回值")
  10. }
  11. def f4(name: String):String={
  12. println("有参,有返回值")
  13. return "hi "+ name
  14. }
  15. def f5(name1:String,name2:String):String={
  16. println("多参,有返回值")
  17. return name1 + name2
  18. }
  19. def f6(name1:String,name2:String):Unit={
  20. println("多参,有返回值")
  21. }

函数参数

  1. 可变参数
  2. 如果参数列表中存在多个参数,那么可变参数一般放置在最后
  3. 参数默认值,一般将有默认值的参数放置在参数列表的后面
  4. 带名参数
  • 可变参数

    1. def function1(str:String*):Unit={
    2. println(str)
    3. }
    4. function1("a")
    5. function1("a","b","c")
    6. //result:
    7. WrappedArray(a)
    8. WrappedArray(a, b, c)
  • 带名参数

    含有大量默认值时候,不依靠原有的顺序去指定参数,非常的方便

  1. def function2(name:String,age:Int):Unit={
  2. println(s"${name}and$age")
  3. }
  4. function2("wy",18)
  5. function2(age = 18,name = "wy")
  6. }
  7. //result:
  8. wyand18
  9. wyand18

函数至简原则

1.return可以省略,Scala会使用函数体的最后一行代码作为返回值
2.如果函数体只有一行代码,可以省略花括号
3.返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
4.如果有return,则不能省略返回值类型,必须指定
5.如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
6.Scala如果期望是无返回值类型,可以省略等号。这种形式称为过程
7.如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
8.如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
9.如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略(匿名函数)

匿名函数

  • 没有名字的函数(x:Int) => {函数体}

    1. val fun = (name:String) => {println(name)}
    2. fun("haha")
    3. def f(func: String => Unit):Unit={
    4. func("rabbit")
    5. }
    6. f(fun)
    7. 匿名对象
    8. f((name:String) => {println(name)})

    匿名函数省略原则

    (1)参数的类型可以省略,会根据形参进行自动的推导;
    f((name) => {println(name)})
    (2)类型省略之后,发现只有一个参数,则圆括号可以省略;
    f(name => {println(name)})

    其他情况:没有参数和参数超过1的永远不能省略圆括号。

(3)匿名函数如果只有一行,则大括号也可以省略;
f(name => println(name))
(4)如果参数只出现一次,则参数省略且后面参数可以用代替。
`f(println(
))<br />(5)如果可以推断出,当前传入的println是一个函数体,而不是调用语句,可以直接省略下划<br />f(println)`

举例:

  1. def run(fun:(Int,Int)=>Int):Int = {
  2. fun(1,2)
  3. }
  4. val add = (a:Int,b:Int) => a+b
  5. val del = (a:Int,b:Int) => a-b
  6. println(run(add))
  7. println(run(del))
  8. //result:
  9. 3
  10. -1

匿名函数可以继续简化
println(run(_+_))
println(run(_-_))

  1. println(run(_+_))
  2. println(run(_-_))
  3. println(run(-_-_))
  4. println(run(+_-_))
  5. //result:
  6. 3
  7. -1
  8. -3
  9. -1

高阶函数

常见方式

  1. def f(n:Int):Int={
  2. println("f调用")
  3. n + 1
  4. }
  5. val result: Int =f(123)
  6. println(result)
  • 函数作为值进行传递

    1. // 把完整的函数传递给f1、f2
    2. val f1 = f _
    3. val f2: Int=>Int = f
    4. println(f1(12))
    5. println(f2(12))
    6. println(f1)
    7. println(f2)
    8. // 本质上是两个不同的引用,也说明了Scala也是面向对象编程
    9. //f调用
    10. //13
    11. //f调用
    12. //13
    13. // Demo01.demo2$$$Lambda$5/6738746@7e0babb1
    14. // Demo01.demo2$$$Lambda$6/2096171631@6debcae2
  • 函数作为参数进行传递

    1. def num(op:(Int,Int)=>Int,a:Int,b:Int):Int={
    2. op(a,b)
    3. }
    4. def add(a:Int,b:Int):Int ={
    5. a+b
    6. }
    7. println(num(add,12,13))
    8. println(num((a,b)=> a+b,12,13))
    9. println(num(_ + _,12,13))
  • 函数作为函数的返回值返回

    1. def f5():Int=>Unit={
    2. def f6(a: Int):Unit={
    3. println("f5调用"+a)
    4. }
    5. f6
    6. }
    7. // 调用f6
    8. val f6 = f5()
    9. println(f5)
    10. println(f6(25))
    11. // 或
    12. println(f5()(25))

    对数组进行处理,将操作对象抽象出来,处理完毕之后将结果返回给一个新的数组

  1. def main(args: Array[String]): Unit = {
  2. // map
  3. val arr:Array[Int] = Array(12,13,14,15)
  4. // 对数组进行处理,将操作对象抽象出来,处理完毕之后将结果返回给一个新的数组
  5. def array(array:Array[Int],op:Int => Int):Array[Int]={
  6. for (elem <- array) yield op(elem)
  7. }
  8. /**
  9. * 要注意的是,接收返回结果的是一个新的数组,op是一个操作,使用for循环对旧数组进行遍历
  10. */
  11. def addOne(elem:Int):Int={elem+1}
  12. val newArray:Array[Int] = array(arr,addOne)
  13. println(newArray.mkString(","))
  14. //传入匿名函数,实现元素翻倍
  15. val newArray2 = array(arr,_*2)
  16. println(newArray2.mkString(","))
  17. }

题目测试

涉及闭包柯里化
image.png

  1. def main(args: Array[String]): Unit = {
  2. def fun =(a:Int,b:String,c:Char)=>{
  3. if (a==0&&b==""&&c=='0') {false}
  4. else {true}
  5. }
  6. println(fun(1,"1",1))
  7. def func(i: Int):String => (Char=>Boolean) ={
  8. def f1(i1:String):Char=>Boolean = {
  9. def f2(i2:Char):Boolean = {
  10. if (i==0&&i1==""&&i2=='0') {false}
  11. else {true}
  12. }
  13. f2
  14. }
  15. f1
  16. }
  17. def func1(i: Int):String =>(Char=>Boolean) ={
  18. i1=> i2=> if (i==0&&i1==""&&i2=='0') false else true
  19. }
  20. //柯里化
  21. def func2(i: Int)(i1:String)(i2:Char):Boolean={
  22. if (i==0&&i1==""&&i2=='0') false else true
  23. }
  24. println(func1(0)("")('0'))
  25. }

函数柯里化&闭包

闭包:函数式编程的标配

1)说明

  • 闭包: 如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
  • 函数柯里化: 把一个参数列表的多个参数,变成多个参数列表。

在Scala语言中,所有的函数都是一个对象实例,放在堆中。使用对象都是对对象的引用

闭包:
image.png
通用性好,适用性差

  1. def add(a:Int):Int =>Int={
  2. def addB(b:Int):Int={
  3. a+b
  4. }
  5. addB
  6. }
  7. println(add(12)(12))
  8. val addby1 = add(1)
  9. val addby2 = add(1)
  10. println(addby1(12))
  11. println(addby2(15))

优化代码:

  1. def add(a: Int): Int => Int = {b => a + b}
  2. def add(a: Int): Int => Int = a + _

柯里化:

一旦用到了柯里化,必定用到了闭包,因为柯里化的底层就是闭包

递归

一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用
举例(阶乘):

  1. def fact(a:Int): Int ={
  2. if (a==0) return 1
  3. fact(a-1) * a
  4. }
  5. println(fact(5))

尾递归实现

  • 使用loop函数取出之前的有效值,舍去无用的数值
  • @tailrec指明该函数为尾递归,用来检查该方法是否为尾递归函数
  • 在Java中不可以,因为这样的实现需要编译器支持
  1. def tailfact(n: Int): Int = {
  2. @tailrec
  3. def loop(n: Int, currRes: Int): Int = {
  4. if (n == 0) {return currRes}
  5. loop(n - 1, currRes * n)
  6. }
  7. loop(n, 1)
  8. }
  9. println(tailfact(5))

控制抽象

针对函数的参数而言,是函数参数中的一个特性。

  • 值调用

把计算后的值传递过去

  1. def f0(a:Int):Unit={
  2. println("a:"+ a)
  3. }
  4. def f1():Int ={
  5. 24
  6. }
  7. f0(f1())
  • 名调用

把代码传递过去

  1. def f1():Int ={
  2. println("f1调用")
  3. 24
  4. }
  5. def f2(a: =>Int):Unit ={
  6. println("a="+a)
  7. println("a="+a)
  8. }
  9. f2(f1())
  10. //result:
  11. f1调用
  12. a=24
  13. f1调用
  14. a=24

自定义while循环

先看一看使用while循环的场景

  1. var num = 12
  2. while(num >= 1){
  3. println(num)
  4. num-=1
  5. }

step1:传递num >= 1
step2:传递println(num)
实现就可以用传名调用

  1. var num = 12
  2. //用匿名函数实现
  3. def myWhile(condition: =>Boolean): (=>Unit)=>Unit={
  4. //内层函数需要递归调用,参数就是循环体
  5. def doLoop(op: =>Unit):Unit={
  6. if(condition){
  7. op
  8. myWhile(condition)(op)
  9. }
  10. }
  11. doLoop _
  12. }
  13. myWhile(num >= 1){
  14. println(num)
  15. num-=1
  16. }

代码简化:

  1. def myWhile(condition: => Boolean): (=> Unit) => Unit = {
  2. op => {
  3. if (condition) {
  4. op
  5. myWhile(condition)(op)
  6. }
  7. }
  8. }

继续简化(柯里化):

  1. def myWhile(condition: => Boolean)(op: =>Unit): Unit = {
  2. if (condition) {
  3. op
  4. myWhile(condition)(op)
  5. }
  6. }

惰性加载

当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数。

  1. //sum被使用时才被调用
  2. lazy val result :Int = sum(12,47)
  3. println("1.函数调用")
  4. println("2.result:"+result)
  5. }
  6. def sum(a: Int, b: Int):Int={
  7. println("3.sum调用")
  8. a + b
  9. }
  10. 1.函数调用
  11. 3.sum调用
  12. 2.result:59