scala学习复习笔记超详细(函数式编程高级)

Scala函数式编程高级

先看一个需求:

给你一个集合val list = List(1, 2, 3, 4, "abc") ,请完成如下要求:

  1. 将集合list中的所有数字+1,并返回一个新的集合。
  2. 要求忽略掉非数字的元素,即返回的新的集合形式为 (2, 3, 4, 5)

解决方式:

  1. filter和map返回新的集合
  2. 模式匹配
package com.atguigu.chapter13

/**
  * @Date 2021/4/3 20:21
  * @Version 10.21
  * @Author DuanChaojie
  */
object PartialFunDemo01 {
  def main(args: Array[String]): Unit = {
    /**
      * 给你一个集合val list = List(1, 2, 3, 4, "abc") ,请完成如下要求:
      * 将集合list中的所有数字+1,并返回一个新的集合
      * 要求忽略掉 非数字 的元素,即返回的 新的集合 形式为 (2, 3, 4, 5)
      */
    /**
      * 思路1 filter + map 方式解决
      * 虽然可以解决问题,但是麻烦.
      */
    val list = List(1, 2, 3, 4, "hello")

    /** 方案一:
      * val res1 = list.filter(filterFun)
      * println("res1 = " + res1)
      *
      * val res2 = res1.map(anyToInt)
      * println("res2 = " + res2)
      *
      * val res3 = res2.map(funAddOne)
      * println("res3 = " + res3)
      */
    val res1 = list.filter(filterFun).map(anyToInt).map(funAddOne)
    println(res1)

    /**
      * 解决方案二:使用模式匹配
      */
    val res2 = list.map(addOne)
    println(res2)
  }

  def addOne(x: Any): Any = {
    x match {
      case n: Int => n + 1
      case _ =>
    }
  }

  /**
    * @param x 输入的类型为Any类型
    * @return 输入的x为Int类型才返回true
    */
  def filterFun(x: Any): Boolean = {
    x.isInstanceOf[Int]
  }

  /**
    * @param n Any类型的参数
    * @return 返回Int
    */
  def anyToInt(n: Any): Int = {
    n.asInstanceOf[Int]
  }

  /**
    * @param n
    * @return n + 1
    */
  def funAddOne(n: Int): Int = {
    n + 1
  }

}

1. 偏函数

  1. 在对符合某个条件,而不是所有情况进行逻辑操作时,使用偏函数是一个不错的选择。
  2. 将包在大括号内的一组case语句封装为函数,我们称之为偏函数,它只对会作用于指定类型的参数或指定范围值的参数实施计算,超出范围的值会忽略(未必会忽略,这取决于你打算怎样处理)
  3. 偏函数在Scala中是一个特质PartialFunction
  4. 使用偏函数解决前面的问题:
/**
  * @Date 2021/4/3 20:36
  * @Version 10.21
  * @Author DuanChaojie
  */
object PartialFunDemo02 {
  def main(args: Array[String]): Unit = {
    //使用偏函数解决
    val list = List(1, 2, 3, 4, "hello")


    /**
      * 定义一个偏函数
      *  1. PartialFunction[Any,Int] 表示偏函数接收的参数类型是Any,返回类型是Int
      *  2. isDefinedAt(x: Any) 如果返回true ,就会去调用 apply 构建对象实例,如果是false,过滤
      *  3. apply 构造器 ,对传入的值 + 1,并返回(新的集合)
      */
    val partialFun = new PartialFunction[Any, Int] { // PartialFunction是一个特质
      override def isDefinedAt(x: Any): Boolean = {
        x.isInstanceOf[Int]
      }

      override def apply(v1: Any): Int = {
        v1.asInstanceOf[Int] + 1
      }
    }

    /**
      * 说明:如果是使用偏函数,则不能使用map,应该使用collect
      * 说明一下偏函数的执行流程
      *  1. 遍历list所有元素
      *  2. 然后调用 val element = if(partialFun-isDefinedAt(list单个元素)) {partialFun-apply(list单个元素) }
      *  3. 每得到一个 element,放入到新的集合,最后返回
      */

    val res = list.collect(partialFun)
    // List(2, 3, 4, 5)
    println(res)

  }
}

偏函数小结:

  1. 使用构建特质的实现类(使用的方式是PartialFunction的匿名子类)
  2. PartialFunction 是个特质(看源码)
  3. 构建偏函数时,参数形式 [Any, Int]是泛型,第一个表示参数类型,第二个表示返回参数
  4. 当使用偏函数时,会遍历集合的所有元素,编译器执行流程时先执行isDefinedAt()如果为true ,就会执行 apply, 构建一个新的Int 对象返回。
  5. 执行isDefinedAt() 为false 就过滤掉这个元素,即不构建新的Int对象。
  6. map函数不支持偏函数,因为map底层的机制就是所有循环遍历,无法过滤处理原来集合的元素
  7. collect函数支持偏函数。
偏函数简化形式

声明偏函数,需要重写特质中的方法,有的时候会略显麻烦,而Scala其实提供了简单的方法


/**
  * @Date 2021/4/3 20:47
  * @Version 10.21
  * @Author DuanChaojie
  */
object PartialFunDemo03 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4, "hello")

    /**
      * 第一种简写方式:使用case
      * @return
      */
    def partialFun1: PartialFunction[Any, Int] = {
      case i: Int => i + 1
    }

    val res1 = list.collect(partialFun1)
    //res1 = List(2, 3, 4, 5)
    println("res1 = " + res1)

    /**
      * 第二种简写方式:
      */
    val res2 = list.collect {
      case i: Int => i + 1
    }
    // res2 = List(2, 3, 4, 5)
    println("res2 = " + res2)
  }
}
作为参数的函数

函数作为一个变量传入到了另一个函数中,那么该作为参数的函数的类型是:function1,即:(参数类型) => 返回类型

/**
  * @Date 2021/4/3 20:55
  * @Version 10.21
  * @Author DuanChaojie
  */
object FunParameterDemo01 {
  def main(args: Array[String]): Unit = {
    def plus(x: Int) = 3 + x
    //val res = Array(3,6,9).map(plus(_))
    val res = Array(3,6,9).map(plus)
      
    // mkString(seq:String)方法是将原字符串使用特定的字符串seq分割。
    // 6,9,12
    println(res.mkString(","))

    /**
      * 在scala中,函数也是有类型,比如plus就是<function1>
      */
    println(plus _)
  }
}

小结:

  1. map(plus(_)) 中的plus(_) 就是将plus这个函数当做一个参数传给了map,_这里代表从集合中遍历出来的一个元素。
  2. plus(_) 这里也可以写成 plus 表示对 Array(1,2,3,4) 遍历,将每次遍历的元素传给plus的 x
  3. 进行 3 + x 运算后,返回新的Int ,并加入到新的集合 res中
  4. def map[B, That](f: A => B) 的声明中的 f: A => B 一个函数

2. 匿名函数

没有名字的函数就是匿名函数,可以通过函数表达式来设置匿名函数。

/**
  * @Date 2021/4/3 21:02
  * @Version 10.21
  * @Author DuanChaojie
  */
object AnonymousFunDemo01 {
  def main(args: Array[String]): Unit = {
    /**
      * 对匿名函数的说明
      *  1. 不需要写def函数名
      *  2. 不需要写返回类型,使用类型推导
      *  3.  =变成=>
      *  4. 如果有多行,则使用{} 包括
      */
    val triple = (x: Double) => {
      println("x = " + x)
      x * x
    }
    // triple(3) = 9.0
    println("triple(3) = " + triple(3))
  }
}

课堂案例:请编写一个匿名函数,可以返回2个整数的和,并输出该匿名函数的类型

/**
  * @Date 2021/4/3 21:41
  * @Version 10.21
  * @Author DuanChaojie
  */
object AnonymousFunDemo02 {
  def main(args: Array[String]): Unit = {
    val sum = (n1: Int, n2: Int) => {
      n1 + n2
    }

    // 36
    println(sum(21, 15))
    // sum函数的类型是:<function2>
    println("sum函数的类型是:" + sum)
  }
}

3. 高阶函数

能够接受函数作为参数的函数,叫做高阶函数 (higher-order function)。可使应用程序更加健壮。

/**
  * @Date 2021/4/3 21:44
  * @Version 10.21
  * @Author DuanChaojie
  */
object HigherOrderFunDemo01 {
  def main(args: Array[String]): Unit = {
    val res = hfun(fun1, fun2, 3.5)
    println("res = " + res)
  }

  /**
    * 高阶函数
    * @param f1
    * @param f2
    * @param n
    * @return 将n进行以下处理返回:(n+n).toInt
    */
  def hfun(f1: Double => Double, f2: Double => Int, n: Double) = {
    f2(f1(n))
  }


  /** 普通函数fun1
    * @param x 输入Double类型的值
    * @return x + x
    */
  def fun1(x: Double): Double = {
    x + x
  }

  /** 普通函数fun2
    * @param x 输入Double类型的值
    * @return 返回对应的Int
    */
  def fun2(x: Double): Int = {
    x.toInt
  }
}

高阶函数可以返回函数类型

/**
  * @Date 2021/4/3 21:52
  * @Version 10.21
  * @Author DuanChaojie
  */
object HigherOrderFunDemo02 {
  def main(args: Array[String]): Unit = {
    /**
      * 这里返回的fun1就是minusxy里面的匿名函数:(y: Int) => 3 + y
      * 等价于 def fun1 = (y: Int) => 3 + y
      * 所以fun1(6)的结果为9
      * 也可以一步到位:minusxy(3)(6)
      */
    val fun1 = minusxy(3)
    // fun1 = <function1>
    println("fun1 = " + fun1)
    val res = fun1(6)
    // res = 9
    println("res = " + res)

    // minusxy(3)(6) = 9
    println("minusxy(3)(6) = " + minusxy(3)(6))

  }

  def minusxy(x: Int) = {
    // 匿名函数
    (y: Int) => x + y
  }
}

4. 类型推断

参数推断省去类型信息(在某些情况下[需要有应用场景],参数类型是可以推断出来的,如list=(1,2,3) list.map() map中函数参数类型是可以推断的),同时也可以进行相应的简写。

参数类型推断写法说明:

  1. 参数类型是可以推断时,可以省略参数类型
  2. 当传入的函数,只有单个参数时,可以省去括号
  3. 如果变量只在=>右边只出现一次,可以用_来代替
/**
  * @Date 2021/4/3 22:04
  * @Version 10.21
  * @Author DuanChaojie
  */
object ParameterInferDemo {
  /**
    * 参数类型是可以推断时,可以省略参数类型
    * 当传入的函数,只有单个参数时,可以省去括号
    * 如果变量只在=>右边只出现一次,可以用_来代替
    */
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4)

    println(list.map((x: Int) => x + 1)) // List(2, 3, 4, 5)
    println(list.map((x) => x + 1)) //List(2, 3, 4, 5)
    println(list.map(x => x + 1)) //List(2, 3, 4, 5)
    println(list.map(_ + 1)) //List(2, 3, 4, 5)


    println(list.reduce(fun1)) // 10
    println(list.reduce((n1: Int, n2: Int) => n1 + n2)) //10
    println(list.reduce((n1, n2) => n1 + n2)) //10
    println(list.reduce(_ + _)) //10
  }

  def fun1(n1: Int, n2: Int): Int = {
    n1 + n2
  }
}
  1. map是一个高阶函数,因此也可以直接传入一个匿名函数,完成map
  2. 当遍历list时,参数类型是可以推断出来的,可以省略数据类型Int println(list.map((x)=>x + 1))
  3. 当传入的函数,只有单个参数时,可以省去括号 println(list.map(x=>x + 1))
  4. 如果变量只在=>右边只出现一次,可以用_来代替 println(list.map(_ + 1))

5. 闭包

闭包就是一个函数与其相关的引用环境组合的一个整体(实体)。

通过一个例子了解闭包:

object Demo {
  def main(args: Array[String]): Unit = {
    val fun1 = minusxy(3)
    val res1 = fun1(6)
    val res2 = fun1(9)
  }

  def minusxy(x: Int) = {
    // 匿名函数
    (y: Int) => x + y
  }
}
  1. (y: Int) => x + y返回的是一个匿名函数 ,因为该函数引用到到函数外的 x,那么该函数和x整体形成一个闭包如:这里 val fun1 = minusxy(3) 的fun1函数就是闭包
  2. 你可以这样理解,返回函数是一个对象,而x就是该对象的一个字段,他们共同形成一个闭包
  3. 当多次调用fun1时(可以理解多次调用闭包),发现使用的是同一个x, 所以x不变。
  4. 在使用闭包时,主要搞清楚返回函数引用了函数外的哪些变量,因为他们会组合成一个整体(实体),形成一个闭包。
闭包的最佳实践

请编写一个程序,具体要求如下:

  1. 编写一个函数 makeSuffix(suffix: String) 可以接收一个文件后缀名(比如.png),并返回一个闭包
  2. 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.png) ,则返回 文件名.png, 如果已经有.png后缀,则返回原文件名。
  3. 要求使用闭包的方式完成
  4. String.endsWith(xx)
/**
  * @Date 2021/4/3 22:13
  * @Version 10.21
  * @Author DuanChaojie
  */
object ClosureDemo01 {
  def main(args: Array[String]): Unit = {
    val fun = makeSuffix(".png")

    // dog.png
    println(fun("dog.png"))
    // cat.png
    println(fun("cat"))
  }

  /**
    * @param suffix 传入
    * @return
    */
  def makeSuffix(suffix: String) = {
    // 返回一个匿名函数,会使用到suffix
    (filename: String) => {
      if (filename.endsWith(suffix)) {
        filename
      } else {
        filename + suffix
      }
    }
  }
}

体会闭包的好处:

  1. 返回的匿名函数和 makeSuffix (suffix string) 的 suffix 变量 组合成一个闭包,因为返回的函数引用到suffix这个变量
  2. 我们体会一下闭包的好处,如果使用传统的方法,也可以轻松实现这个功能,但是传统方法需要每次都传入 后缀名,比如 .png ,而闭包因为可以保留上次引用的某个值,所以我们传入一次就可以反复使用。大家可以仔细的体会一把。

6. 函数柯里化(curry)

  1. 函数编程中,接受多个参数的函数都可以转化为接受单个参数的函数,这个转化过程就叫柯里化。
  2. 柯里化就是证明了函数只需要一个参数而已。其实我们刚才的学习过程中,已经涉及到了柯里化操作。
  3. 不用设立柯里化存在的意义这样的命题。柯里化就是以函数为主体这种思想发展的必然产生的结果。(即:柯里化是面向函数思想的必然产生结果)
函数柯里化快速入门

编写一个函数,接收两个整数,可以返回两个数的乘积,要求:

  1. 使用常规的方式完成
  2. 使用闭包的方式完成
  3. 使用函数柯里化完成
/**
  * @Date 2021/4/3 22:20
  * @Version 10.21
  * @Author DuanChaojie
  */
object CurryDemo01 {
  def main(args: Array[String]): Unit = {

    // 使用常规的方式完成
    def curry1(x: Int, y: Int) = x * y
    println(curry1(10, 10))

    // 使用闭包的方式完成
    def curry2(x: Int) = (y: Int) => x * y
    println(curry2(10)(10))

    // 使用函数柯里化完成
    def curry3(x: Int)(y: Int) = x * y
    println(curry3(10)(10))
  }
}
函数柯里化最佳实践

比较两个字符串在忽略大小写的情况下是否相等,注意,这里是两个任务:

  1. 全部转大写(或小写)
  2. 比较是否相等

针对这两个操作,我们用一个函数去处理的思想,其实也变成了两个函数处理的思想(柯里化)

/**
  * @Date 2021/4/3 22:23
  * @Version 10.21
  * @Author DuanChaojie
  */
object CurryDemo02 {
  def main(args: Array[String]): Unit = {
    val str = "ddaimm"
    // false
    println(str.compareStr("HELLO")(fun))

    // true
    println(str.compareStr("ddaimm")(_.equals(_)))
  }

  /**
    * 可以接收两个字符串,比较是否相等
    */
  def fun(str1: String, str2: String): Boolean = {
    str1.equals(str2)
  }

  /**
    * 隐式类
    */
  implicit class compare(str1: String) {
    /**
      * 体现了将比较字符串的事情,分解成两个任务完成
      * 1. compareStr 转换大小写
      * 2. fun函数完成比较任务
      */
    def compareStr(str2: String)(fun: (String, String) => Boolean): Boolean = {
      fun(str2.toUpperCase(), str1.toUpperCase())
    }
  }

}

7. 控制抽象

如何实现将一段代码(从形式上看),作为参数传递给高阶函数,在高阶函数内部执行这段代码.,其使用的形式如 breakable{} 。

    var n = 10
    breakable {
        while (n <= 20) {
            n += 1
            if (n == 18) {
              break()
            }
        }
    }

控制抽象是这样的函数,满足如下条件

  1. 参数是函数
  2. 函数参数没有输入值也没有返回值
/**
  * @Date 2021/4/3 22:37
  * @Version 10.21
  * @Author DuanChaojie
  */
object AbstractControlDemo01 {
  def main(args: Array[String]): Unit = {
    /**
      * myRunInThread 就是一个抽象控制
      * 是没有输入, 也没有输出的函数 f1: () => Unit
      */
    def myRunInThread(f1: () => Unit) = {
      new Thread {
        override def run(): Unit = {
          f1() //只写了 f1
        }
      }.start()
    }

    myRunInThread { () => {
      println("mm干活咯!5秒完成...")
      Thread.sleep(5000)
      println("mm干完咯!")
    }
    }

    //简写形式
    def myRunInThread2(f1: => Unit) = {
      new Thread {
        override def run(): Unit = {
          f1 //只写了 f1
        }
      }.start()
    }

    // 对于没有输入,也没有返回值函数,可以简写成如下形式
    myRunInThread2 {
      println("dd干活咯!5秒完成...")
      Thread.sleep(5000)
      println("dd干完咯!")
    }

  }
}

进阶用法:实现类似while的until函数

/**
  * @Date 2021/4/3 22:44
  * @Version 10.21
  * @Author DuanChaojie
  */
object AbstractControlDemo02 {
  def main(args: Array[String]): Unit = {

    var x = 10

    until(x == 0) {
      x -= 1
      println("x=" + x)
    }

  }

  /** 说明
    * 1 函数名为 until , 实现了类似 while循环的效果
    * 2. condition: => Boolean 是后一个没有输入值,返回Boolean类型函数
    * 3. block: => Unit 没有输入值,也没有返回值的韩
    */
  def until(condition: => Boolean)(block: => Unit): Unit = {
    //类似while循环,递归
    if (!condition) {
      block // x= 9 ,x = 8 x =7 ....
      until(condition)(block)
    }

  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱敲代码的小黑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值