第三课 尚硅谷Scala语言学习-函数式编程

第三课 尚硅谷Scala语言学习-函数式编程

第一节 函数基础

1.1 面向对象和面向过程

  1. 面向对象编程。解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。
    • 对象:用户
    • 行为:登录、连接 JDBC、读取数据库
    • 属性:用户名、密码
  2. 函数式编程。解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
    • 例如:请求->用户名、密码->连接 JDBC->读取数据库
  3. Scala语言是一个完全面向对象编程语言。万物皆对象, 对象的本质:对数据和行为的一个封装
  4. Scala 语言是一个完全函数式编程语言。万物皆函数。函数的本质:函数可以当做一个值进行传递
  5. Scala 中函数式编程和面向对象编程完美融合在一起了。

1.2 函数介绍

在这里插入图片描述

  1. 函数的基本语法。
object TestFunction {
  def main (args: Array[String]): Unit = {
    // 函数定义
    def f(arg: String): Unit = {
      println(arg)
    }
    f("hello world")
  }
}
  1. 函数和方法的区别
    • 核心概念
      • 为完成某一功能的程序语句的集合,称为函数。
      • 类中的函数称之方法。
    • 案例实操
      • Scala 语言可以在任何的语法结构中声明任何的语法
      • 函数没有重载和重写的概念;方法可以进行重载和重写
      • Scala 中函数可以嵌套定义
object TestFunction {
  // 方法可以进行重载和重写
  def main(): Unit = {
  }

  def main (args: Array[String]): Unit = {
    new Date()

    // 函数定义 函数不能重载和重写,程序会报错
    def test(): Unit = {
      println("无参, 无返回值")
    }

    def test2(): Unit ={
      def test3(name:String):Unit={
        println("函数可以嵌套定义")
      }
    }
  }
}

1.3 函数常用定义

  1. 函数定义
    • 函数 1:无参,无返回值
    • 函数 2:无参,有返回值
    • 函数 3:有参,无返回值
    • 函数 4:有参,有返回值
    • 函数 5:多参,无返回值
    • 函数 6:多参,有返回值
object Test02_FunctionDefine {
  def main(args: Array[String]): Unit = {
    // 函数1:无参,无返回值
    def f1(): Unit = {
      println("1. 无参,无返回值")
    }
    f1()
    println(f1())

    println("=========================")

    // 函数2:无参,有返回值
    def f2(): Int = {
      println("2. 无参,有返回值")
      return 12
    }
    println(f2())
    println("=========================")

    //  函数3:有参,无返回值
    def f3(name: String): Unit = {
      println("3:有参,无返回值 " + name)
    }
    println(f3("alice"))
    println("=========================")

    // 函数4:有参,有返回值
    def f4(name: String): String = {
      println("4:有参,有返回值 " + name)
      return "hi, " + name
    }
    println(f4("alice"))
    println("=========================")

    //  函数5:多参,无返回值
    def f5(name1: String, name2: String): Unit = {
      println("5:多参,无返回值")
      println(s"${name1}${name2}都是我的好朋友")
    }
    f5("alice","bob")
    println("=========================")

    //    (6)函数6:多参,有返回值
    def f6(a: Int, b: Int): Int = {
      println("6:多参,有返回值")
      return a + b
    }
    println(f6(12, 37))
  }
}

1.4 函数参数

  1. 案例实操
    • 可变参数
    • 如果参数列表中存在多个参数,那么可变参数一般放置在最后
    • 参数默认值,一般将有默认值的参数放置在参数列表的后面
    • 带名参数
object Test03_FunctionParameter {
  def main(args: Array[String]): Unit = {
    // 可变参数
    def f1(str: String*): Unit = {
      println(str)
    }
    f1("alice")
    f1("aaa", "bbb", "ccc")

    //  如果参数列表中存在多个参数,那么可变参数一般放置在最后
    def f2(str1: String, str2: String*): Unit = {
      println("str1: " + str1 + " str2: " + str2)
    }
    f2("alice")
    f2("aaa", "bbb", "ccc")

    // 参数默认值,一般将有默认值的参数放置在参数列表的后面
    def f3(name: String = "atguigu"): Unit = {
      println("My school is " + name)
    }

    f3("school")
    f3()

    // 带名参数
    def f4(name: String = "atguigu", age: Int): Unit = {
      println(s"${age}岁的${name}在尚硅谷学习")
    }
    f4("alice", 20)
    f4(age = 23, name = "bob")
    f4(age = 21)
  }
}

1.5 函数至简原则

  1. 函数至简原则:能省则省。至简原则细节如下
    • return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
    • 如果函数体只有一行代码,可以省略花括号
    • 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
    • 如果有 return,则不能省略返回值类型,必须指定
    • 如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
    • Scala 如果期望是无返回值类型,可以省略等号
    • 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
    • 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
    • 如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
// 函数至简原则
object Test04_Simplify {
  def main(args: Array[String]): Unit = {
    def f0(name: String): String = {
      return name
    }
    println(f0("atguigu"))

    println("==========================")

    // 1. return可以省略,Scala会使用函数体的最后一行代码作为返回值
    def f1(name: String): String = {
      name
    }
    println(f1("atguigu"))
    println("==========================")

    // 2.如果函数体只有一行代码,可以省略花括号
    def f2(name: String): String = name
    println(f2("atguigu"))
    println("==========================")

    // 3. 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
    def f3(name: String) = name
    println(f3("atguigu"))
    println("==========================")

    //  4. 如果有return,则不能省略返回值类型,必须指定
//    def f4(name: String) = {
//      return name
//    }
//
//    println(f4("atguigu"))
    println("==========================")

    //  5. 如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
    def f5(name: String): Unit = {
      return name
    }
    println(f5("atguigu"))
    println("==========================")

    // 6. Scala如果期望是无返回值类型,可以省略等号
    def f6(name: String) {
      println(name)
    }
    println(f6("atguigu"))
    println("==========================")

    //  7. 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
    def f7(): Unit = {
      println("atguigu")
    }
    f7()
    f7
    println("==========================")

    // 8. 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
    def f8: Unit = {
      println("atguigu")
    }
    //    f8()
    f8
    println("==========================")

    // 9. 如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
    def f9(name: String): Unit = {
      println(name)
    }

    // 匿名函数,lambda表达式
    (name: String) => { println(name) }
    println("==========================")
  }
}

第二节 函数进阶

2.1 高阶函数

  1. 函数可以作为值进行传递
  2. 函数作为参数进行传递
  3. 函数作为函数的返回值返回
object Test06_HighOrderFunction {
  def main(args: Array[String]): Unit = {
    def f(n: Int): Int = {
      println("f调用")
      n + 1
    }
    def fun(): Int = {
      println("fun调用")
      1
    }

    val result: Int = f(123)
    println(result)

    // 1. 函数作为值进行传递
    val f1: Int=>Int = f
    // 被调用函数f后面加上_,相当于把函数f当成一个整体
    val f2 = f _

    println(f1)
    println(f1(12))
    println(f2)
    println(f2(35))

    val f3: ()=>Int = fun
    val f4 = fun _
    println(f3)
    println(f4)

    // 2. 函数作为参数进行传递
    // 定义二元计算函数
    def dualEval(op: (Int, Int)=>Int, a: Int, b: Int): Int = {
      op(a, b)
    }

    def add(a: Int, b: Int): Int = {
      a + b
    }

    println(dualEval(add, 12, 35))
    println(dualEval((a, b) => a + b, 12, 35))
    println(dualEval(_ + _, 12, 35))

    // 3. 函数作为函数的返回值返回
    def f5(): Int=>Unit = {
      def f6(a: Int): Unit = {
        println("f6调用 " + a)
      }
      f6    // 将函数直接返回
    }

//    val f6 = f5()
//    println(f6)
//    println(f6(25))
    println(f5()(25))
  }
}

2.2 匿名函数

  1. 没有名字的函数就是匿名函数。
// x:表示输入参数类型;Int:表示输入参数类型;函数体:表示具体代码逻辑
(x:Int)=>{函数体}
  1. 案例实操
    • 参数的类型可以省略,会根据形参进行自动的推导
    • 类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。
    • 匿名函数如果只有一行,则大括号也可以省略
    • 如果参数只出现一次,则参数省略且后面参数可以用_代替
object Test05_Lambda {
  def main(args: Array[String]): Unit = {
    val fun = (name: String) => {
      println(name)
    }
    fun("atguigu")
    println("========================")

    // 定义一个函数,以函数作为参数输入
    def f(func: String => Unit): Unit = {
      func("atguigu")
    }
    f(fun)
    f((name: String) => {
      println(name)
    })
    println("========================")

    // 匿名函数的简化原则
    // 1. 参数的类型可以省略,会根据形参进行自动的推导
    f((name) => {
      println(name)
    })

    // 2. 类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。
    f( name => {
      println(name)
    })

    // 3. 匿名函数如果只有一行,则大括号也可以省略
    f( name => println(name) )

    // 4. 如果参数只出现一次,则参数省略且后面参数可以用_代替
    f( println(_) )

    // 5. 如果可以推断出,当前传入的println是一个函数体,而不是调用语句,可以直接省略下划线
    f( println )
    println("=========================")

    // 实际示例,定义一个”二元运算“函数,只操作1和2两个数,但是具体运算通过参数传入
    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))

    println(dualFunctionOneAndTwo((a, b) => a + b))
    println(dualFunctionOneAndTwo( _ + _))
    println(dualFunctionOneAndTwo( _ - _))

    println(dualFunctionOneAndTwo((a, b) => b - a))
    println(dualFunctionOneAndTwo( -_ + _))
  }
}
  1. 扩展练习
    • 练习 1:定义一个匿名函数,并将它作为值赋给变量 fun。函数有三个参数,类型分别为 Int,String,Char,返回值类型为 Boolean。要求调用函数 fun(0, “”, ‘0’)得到返回值为 false,其它情况均返回 true。
    • 练习 2: 定义一个函数 func,它接收一个 Int 类型的参数,返回一个函数(记作 f1)。它返回的函数 f1,接收一个 String 类型的参数,同样返回一个函数(记作 f2)。函数 f2 接收一个 Char 类型的参数,返回一个 Boolean 的值。要求调用函数 func(0) (“”) (‘0’)得到返回值为 false,其它情况均返回 true。
package chapter05

object Test08_Practice {
  def main(args: Array[String]): Unit = {
    // 1. 练习1
    val fun = (i: Int, s: String, c: Char) => {
      if (i == 0 && s == "" && c == '0') false else true
    }

    println(fun(0, "", '0'))
    println(fun(0, "", '1'))
    println(fun(23, "", '0'))
    println(fun(0, "hello", '0'))

    println("===========================")

    // 2. 练习2
    def func(i: Int): String=>(Char=>Boolean) = {
      def f1(s: String): Char=>Boolean = {
        def f2(c: Char): Boolean = {
          if (i == 0 && s == "" && c == '0') false else true
        }
        f2
      }
      f1
    }

    println(func(0)("")('0'))
    println(func(0)("")('1'))
    println(func(23)("")('0'))
    println(func(0)("hello")('0'))

    // 匿名函数简写
    def func1(i: Int): String=>(Char=>Boolean) = {
      s => c => if (i == 0 && s == "" && c == '0') false else true
    }

    println(func1(0)("")('0'))
    println(func1(0)("")('1'))
    println(func1(23)("")('0'))
    println(func1(0)("hello")('0'))

    // 柯里化
    def func2(i: Int)(s: String)(c: Char): Boolean = {
      if (i == 0 && s == "" && c == '0') false else true
    }
    println(func2(0)("")('0'))
    println(func2(0)("")('1'))
    println(func2(23)("")('0'))
    println(func2(0)("hello")('0'))
  }
}

2.3 函数柯里化&闭包

  1. 闭包:函数式编程的标配。闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
  2. 函数柯里化:把一个参数列表的多个参数,变成多个参数列表。一旦用了柯里化的表达,它的地层就一定是闭包。
package chapter05

object Test09_ClosureAndCurrying {
  def main(args: Array[String]): Unit = {
    def add(a: Int, b: Int): Int = {
      a + b
    }

    // 1. 考虑固定一个加数的场景
    def addByFour(b: Int): Int = {
      4 + b
    }

    // 2. 扩展固定加数改变的情况
    def addByFive(b: Int): Int = {
      5 + b
    }

    // 3. 将固定加数作为另一个参数传入,但是是作为”第一层参数“传入
    def addByFour1(): 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(35)(24))

    val addByFour2 = addByA(4)
    val addByFive2 = addByA(5)

    println(addByFour2(13))
    println(addByFive2(25))

    // 4. lambda表达式简写
    def addByA1(a: Int): Int=>Int = {
      (b: Int) => {
        a + b
      }
    }
    def addByA2(a: Int): Int=>Int = {
      b => a + b
    }

    def addByA3(a: Int): Int=>Int = a + _
    val addByFour3 = addByA3(4)
    val addByFive3 = addByA3(5)

    println(addByFour3(13))
    println(addByFive3(25))

    // 5. 柯里化
    def addCurrying(a: Int)(b: Int): Int = {
      a + b
    }

    println(addCurrying(35)(24))
  }
}

2.4 递归

  1. 一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用
  2. 尾递归调用,用一个变量解决频繁创栈等待的问题。
package chapter05

import scala.annotation.tailrec

object Test10_Recursion {
  //递归算法
  //1. 方法调用自身
  //2. 方法必须要有跳出的逻辑
  //3. 方法调用自身时,传递的参数应该有规律
  //4. scala中的递归必须声明函数返回值类型
  def main(args: Array[String]): Unit = {
    println(fact(5))
    println(tailFact(5))
  }

  // 递归实现计算阶乘 这种栈空间一层层等待叠加容易爆栈
  def fact(n: Int): Int = {
    if (n == 0) return 1
    fact(n - 1) * n
  }

  // 尾递归实现 每次栈都覆盖上一次的栈 栈使用只使用一次
  def tailFact(n: Int): Int = {
    @tailrec
    def loop(n: Int, currRes: Int): Int = {
      if (n == 0) return currRes
      loop(n - 1, currRes * n)
    }
    loop(n, 1)
  }
}

2.5 控制抽象

  1. 值调用:把计算后的值传递过去
  2. 名调用:把代码传递过去
package chapter05

object Test11_ControlAbstraction {
  def main(args: Array[String]): Unit = {
    // 1. 传值参数
    def f0(a: Int): Unit = {
      println("a: " + a)
      println("a: " + a)
    }
    f0(23)

    def f1(): Int = {
      println("f1调用")
      12
    }
    f0(f1())

    println("========================")

    // 2. 传名参数,传递的不再是具体的值,而是代码块 这里代码块的返回值是Int
    def f2(a: =>Int): Unit = {
      println("a: " + a)
      println("a: " + a)
    }

    f2(23)
    f2(f1())

    f2({
      println("这是一个代码块")
      29
    })

  }
}
  1. 案例实操
package chapter05

object Test12_MyWhile {
  def main(args: Array[String]): Unit = {
    var n = 10

    // 1. 常规的while循环
    while (n >= 1){
      println(n)
      n -= 1
    }

    // 2. 用闭包实现一个函数,将代码块作为参数传入,递归调用
    def myWhile(condition: =>Boolean): (=>Unit)=>Unit = {
      // 内层函数需要递归调用,参数就是循环体
      def doLoop(op: =>Unit): Unit = {
        if (condition){
          op
          myWhile(condition)(op)
        }
      }
      doLoop _
    }

    println("=================")
    n = 10
    myWhile(n >= 1){
      println(n)
      n -= 1
    }

    // 3. 用匿名函数实现
    def myWhile2(condition: =>Boolean): (=>Unit)=>Unit = {
      // 内层函数需要递归调用,参数就是循环体
      op => {
        if (condition){
          op
          myWhile2(condition)(op)
        }
      }
    }
    println("=================")
    n = 10
    myWhile2(n >= 1){
      println(n)
      n -= 1
    }

    // 3. 用柯里化实现
    def myWhile3(condition: =>Boolean)(op: =>Unit): Unit = {
      if (condition){
        op
        myWhile3(condition)(op)
      }
    }

    println("=================")
    n = 10
    myWhile3(n >= 1){
      println(n)
      n -= 1
    }
  }
}

2.6 惰性加载

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

object Test13_Lazy {
  def main(args: Array[String]): Unit = {
    lazy val result: Int = sum(13, 47)

    println("1. 函数调用")
    println("2. result = " + result)
    println("4. result = " + result)
  }

  def sum(a: Int, b: Int): Int = {
    println("3. sum调用")
    a + b
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值