(二)Scala语言——函数式编程

接上篇:(一)Scala语言——入门

一、函数式编程

1)面向对象编程
 解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。
 对象:用户;
 行为:登录、连接jdbc、读取数据库。
 属性:用户名、密码。
Scala语言是一个完全面向对象编程语言。万物皆对象。

2)函数式编程
 解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
 例如:请求->用户名、密码->连接jdbc->读取数据库
Scala语言是一个完全函数式编程语言。万物皆函数(万物皆函数不太对)
 函数可以当做一个值进行传递,高阶函数。
3)在Scala中函数式编程和面向对象完美编程融合在一起了。

4)函数(算子),方法:
 其实是一个东西,只是场景不一样,叫法不一样,在函数式编程习惯叫函数。

1. 函数基本语法

函数的签名:
 def add(a:Int,b:Int):Int
 三要素:函数名 形参列表 返回值类型

  1. 基本语法
    在这里插入图片描述
  2. 实操
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)
  }
}

名词解释
一、纯函数: 特点:

  1. 不产生副作用。 常见的副作用:打印到控制台,修改了外部变量的值,向磁盘写入文件,…
  2. 引用透明, 函数的返回值,只和形参有关,和其他任何的值没有关系 纯函数的好处:天然的支持高并发,

二、只有副作用,没有返回值,叫过程。

细节:

  1. 函数体内可以没有return,这个时候会自动的把最后一行代码的值返回。

  2. 返回值类型也可以省略(:和返回值类型一起省),scala 会根据最后一行代码的值进行推导返回值类型。

  3. 如果有return, 则不能省略返回值类型,必须指定

  4. = 省略的话,表示这个函数一定返回的是 Unit, 不管函数体怎么定义,一定返回Unit。
    返回Unit 的函数, 有时候称为过程。

  5. 如果函数的形参格式是0,则调用的时候,可以省略()

  6. 如果没有形参,则声明的时候也可以省略(),调用的就不能再有圆括号,和val的声明要区别。

  7. 可变参数

  8. 参数的默认值
    当传递时不传,则会使用默认值。

  9. 命名参数:
    调用的时候,指定形参的名字。

2. 函数和方法的区别

个人理解是方法和函数其实是一个东西,只是场景不一样,叫法不一样,在函数式编程习惯叫函数。
(1)Scala语言的语法非常灵活,可以在任何的语法结构中声明任何的语法
(2)函数没有重载和重写的概念;方法可以进行重载和重写
(3)scala中函数可以嵌套定义

3. 函数声明和函数参数

  1. 函数的声明
    (1)函数1:无参,无返回值
    (2)函数2:无参,有返回值
    (3)函数3:有参,无返回值
    (4)函数4:有参,有返回值
    (5)函数5:多参,无返回值
  2. 实例实操
    (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 //用一次求一次
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值