前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >scala(六) 高阶函数

scala(六) 高阶函数

作者头像
用户1483438
发布2022-04-13 15:38:43
6340
发布2022-04-13 15:38:43
举报
文章被收录于专栏:大数据共享大数据共享

介绍

高阶函数:以函数作为参数或返回值的方法(函数)称为高阶函数。我的理解是高阶函数是一种思想,它的作用能让我们的程序更加灵活。 思考:如果让你实现一个计算器,功能不多,只有+-*/ 四个功能。 方式一:普通方式 定义四个方法(函数)

代码语言:javascript
复制
  def main(args:Array[String]):Unit={

    // 加法
    def add(x:Int,y:Int)=x+y

    // 减法
    def minus(x:Int,y:Int)=x-y

    // 乘法
    def multiply(x:Int,y:Int)=x-y

    // 除法
    def division(x:Int,y:Int)=x/y
    
  }

编写一个计算器方法,负责统一调用

代码语言:javascript
复制
    def calculator(x:Int,y:Int,options:String):Int={
      if(options=="+"){
        add(x,y)
      }else if(options=="-"){
        minus(x,y)
      }else if(options=="*"){
        multiply(x,y)
      }else if(options=="/"){
        division(x,y)
      }else{
        println("不支持该功能")
        -1
      }
    }

这种方式简单,但是限制太多了,要是在加一个功能,如取余的方法(函数),那么就需要重新定义一个取余的方法(函数)。要是再来1020个功能呢?难道需要定义这么多方法(函数)?

方式二:高阶函数一

当你看到高阶函数的时候,应该对函数有一定的了解。先不说scala是一个完全面向对象的语言,就函数而言,它本身就是一个对象。FuncationN 对象,N 表示0-22 个数字(不太了解,可能会有点绕)。

既然函数是个对象,那么肯定可以作为参数用来传递。 于是我们可以定义一个计算器函数: 除了传递正常的数值外,还需要把你具体的实现传递给我。

代码语言:javascript
复制
    /**
     * 计算器
     * @param x 参数 1
     * @param y 参数 2
     * @param func 函数对象
     * @return
     */
    def calculator(x:Int,y:Int,func:(Int,Int)=>Int):Int={
      // 具体的功能有函数来实现
      func(x,y)
    }

完整代码

代码语言:javascript
复制
  def main(args:Array[String]):Unit={

    // 加法
    def add(x:Int,y:Int)=x+y

    // 减法
    def minus(x:Int,y:Int)=x-y


    // 乘法
    def multiply(x:Int,y:Int)=x-y

    // 除法
    def division(x:Int,y:Int)=x/y


    /**
     * 计算器
     * @param x 参数 1
     * @param y 参数 2
     * @param func 函数对象
     * @return
     */
    def calculator(x:Int,y:Int,func:(Int,Int)=>Int):Int={
      // 具体的功能有函数来实现
      func(x,y)
    }
    // 除法
    val result=calculator(12,4,division) // 3
    println(result)
  }

比如若现在需要使用加法;我们只需要更改 funcadd 即可。

代码语言:javascript
复制
    val result=calculator(12,4,add)

    println(result) // 16

这种方式:更加灵活,思想更加活跃,最开始接触高阶函数的时候也是一脸懵逼,依样画葫芦的写,达不到活学活用,觉得特难。至于什么时候想明白的我也不知道,接触多了就自然而然就明白了。

回到正题,你是否还有疑问?即便是将函数以参数的形式执行,不是还得先把函数定义好吗?10个20个功能还不是得继续加代码,写函数。

哈哈确实是,但是这种思想,我们应该要明白,否则看到别人的写的函数以这样的形式都不知道啥意思就奇怪了。


对高阶函数有了一定了解后,就来玩玩scala中的高阶函数;看看他你能玩出什么花来。 还是以这个为例

代码语言:javascript
复制
    // 加法
    def add(x:Int,y:Int)=x+y

    // 减法
    def minus(x:Int,y:Int)=x-y

    // 乘法
    def multiply(x:Int,y:Int)=x-y

    // 除法
    def division(x:Int,y:Int)=x/y

你觉得 写这种(如上)太麻烦了,就优化一下;上面是以方法的形式,带有def 关键字。这里改成函数的形式。看起来更加像一个参数,有木有。 函数和方法其实是一样的,只不过表达形式不同,没必要太纠结。

代码语言:javascript
复制
  def main(args:Array[String]):Unit={

    // 加法
    val add=(x:Int,y:Int)=>x+y
    // 减法
    val minus=(x:Int,y:Int)=>x-y

    // 乘法
    val multiply=(x:Int,y:Int)=>x*y

    // 除法
    val division=(x:Int,y:Int)=>x/y


    /**
     * 计算器
     * @param x 参数 1
     * @param y 参数 2
     * @param func 函数对象
     * @return
     */
    def calculator(x:Int,y:Int,func:(Int,Int)=>Int):Int={
      // 具体的功能有函数来实现
      func(x,y)
    }


    val result=calculator(12,4,multiply)
    println(result) // 48
  }

觉得没啥感觉?那我就删了,

代码语言:javascript
复制
  def main(args:Array[String]):Unit={


    /**
     * 计算器
     * @param x 参数 1
     * @param y 参数 2
     * @param func 函数对象
     * @return
     */
    def calculator(x:Int,y:Int,func:(Int,Int)=>Int):Int={
      // 具体的功能有函数来实现
      func(x,y)
    }


    val result=calculator(12,4,_+_)  

    println(result) //16

  }

换成除法:

代码语言:javascript
复制
    val result=calculator(12,4,_/_)  
    println(result) //3

这种方式,够离谱了吧,改到最后都不认识了。 sacla 中有很多地方都会用到下划线_;其用途很强大的,就说说这里的_的作用。在这里,它可以作为一个参数,拿_/_为例,第一个_就代表参数x的值,第二个_就代表参数y的值。

浅谈 Scala 中下划线的用途

高阶函数简化(调用时简化)

  1. 标准写入(以上面的案例说明)
代码语言:javascript
复制
    // 定义函数
    val add=(x:Int,y:Int)=>x+y
    // 调用
    val result=calculator(12,4,add)
    // 输出结果
    println(result)  // 16
  1. 直接传递函数值
代码语言:javascript
复制
    // 调用
    val result=calculator(12,4,(x:Int,y:Int)=>x+y)
    // 输出结果
    println(result)
  1. 参数类型一致,类型可以省略 (x:Int,y:Int)=>x+y 与 func:(Int,Int) 是一致的。
代码语言:javascript
复制
    // 调用
    val result=calculator(12,4,(x,y)=>x+y)
    // 输出结果
    println(result)
  1. 如果函数的参数在函数体中,只使用过一次,可以使用_ 代替。
代码语言:javascript
复制
    // 调用
    val result=calculator(12,4,_+_)
    // 输出结果
    println(result)
  1. 如果 函数参数只有一个,小括号可以省略
代码语言:javascript
复制
 def sayHello(str:String,func:(String)=>String): String ={
      str
 }
 println(sayHello("hello",s=>s)) // hello

'_'的限制场景

当然使用 _ 是有限制的,有些场景不可使用。

  1. 如果函数参数的使用顺序与参数定义的顺序不一样,此时不可用使用下划线代替。 如下: 这种情况就就不能使用下划线代替,会再次数据错误。
代码语言:javascript
复制
val result=calculator(12,4,(x,y)=>y-x)
  1. 如果函数体中有(),函数的参数在函数中小括号中以表达式形式存在,此时不能用小括号代替。
代码语言:javascript
复制
val result=calculator(12,4,(x,y)=>(x+1)*y)
  1. 如果函数只有一个参数,并且在函数体中对参数没有做任何操作就直接返回的时候,不能使用_ 代替。
代码语言:javascript
复制
    def sayHello(str:String,func:(String)=>String): String ={
      str
    }

    // 普通方式
    println("普通方式:",sayHello("hello",(s)=>s))
    // 下划线的方式:
    println("下划线的方式:",sayHello("hello",_))

输出:

代码语言:javascript
复制
(普通方式:,hello)
(下划线的方式:,Demo02$$$Lambda$7/932607259@67b64c45)

思考:为什么输出的是 Demo02$$$Lambda$7/932607259@67b64c45?当如果是第三种情况是,_ 将不再是hello;而是 该函数的引用; 谁的引用?答案就是 sayHello 的引用,也就是当前函数的引用。

案例实战

  1. **对数组中每个元素按照指定规则进行操作,操作之后返回结果结果 数据: Array[String]("hello","spark","hadoop","flink") 规则 [可变]: 通过该规则可以获取到 字符长度首写字母转换大小写 获取每个元素的长度 案例一:获取元素长度
代码语言:javascript
复制
  def main(args: Array[String]): Unit = {
    val arr=Array[String]("hello","spark","hadoop","flink")
    println(map(arr).toList)
  }

  /**
   * 获取元素长度
   * @param array
   */
  def map(array: Array[String]): Array[AnyVal] ={
    for (e<- array)yield e.length
  }
代码语言:javascript
复制
List(5, 5, 6, 5)

该规则是可变的,上面的案例是获取长度,要是获取其他信息又怎么获取呢?学习完高阶函数之后,我们自然可以想到将一个函数作为参数,将规则定义到函数中,至于什么规则,不用操心,传进来是什么就是什么就可以了。 代码优化:优化之后,将规则作为参数传进来。

代码语言:javascript
复制
  def main(args: Array[String]): Unit = {

    val arr=Array[String]("hello","spark","hadoop","flink")
    println(map(arr,(s)=>{s.length}).toList)

  }

  def map(array: Array[String],func:String=>Any): Array[Any] ={
    for (e<- array)yield func(e)
  }
代码语言:javascript
复制
List(5, 5, 6, 5)

咦?感觉没什么?这次需求,获取列表中的首字母

代码语言:javascript
复制
  def main(args: Array[String]): Unit = {

    val arr=Array[String]("hello","spark","hadoop","flink")
    println(map(arr,(s)=>{s.charAt(0)}).toList)

  }
  def map(array: Array[String],func:String=>Any): Array[Any] ={
    for (e<- array)yield func(e)
  }
代码语言:javascript
复制
List(h, s, h, f)

也可以这样,将字符串全部大写

代码语言:javascript
复制
  def main(args: Array[String]): Unit = {

    val arr=Array[String]("hello","spark","hadoop","flink")
    println(map(arr,(s)=>{s.toUpperCase()}).toList)
  }
  def map(array: Array[String],func:String=>Any): Array[Any] ={
    for (e<- array)yield func(e)
  }
代码语言:javascript
复制
List(HELLO, SPARK, HADOOP, FLINK)

知道为什么需要返回 Any 吗?其主要原因是不知道规则是什么。比如:第一次是长度 类型肯定是Int,第一次是首字母肯定的是Char,第三次是转转大写,是一个String。就是因为不确定,所以需要指定为Any

接下来就是代码优化:

  1. 由于参数只有一个,所以可以不用带()
代码语言:javascript
复制
println(map(arr,s=>{s.toUpperCase()}).toList)
  1. 块中只有语句代码所以可以不用指定{}
代码语言:javascript
复制
 println(map(arr,s=>s.toUpperCase()).toList)
  1. 当然方法没参数,也可以不用带()
代码语言:javascript
复制
println(map(arr,s=>s.toUpperCase).toList)
  1. 参数只有一个且没有重复使用的地方,所以可以使用_代替
代码语言:javascript
复制
 println(map(arr,_.toUpperCase).toList)

运行结果:

代码语言:javascript
复制
List(HELLO, SPARK, HADOOP, FLINK)

  1. 对数组中的数据按照指定规则过滤 数据: Array[Int](1,4,2,7,9,10) 规则: [可变] 通过此规则,可以获取,奇偶数3的倍数 等。 案例一:获取所有的奇数
代码语言:javascript
复制
  def main(args: Array[String]): Unit = {
    val arr=Array[Int](1,4,2,7,9,10)

    println(filter(arr).toList)
  }

  /**
   * 过滤
   * @param arr
   * @return
   */
  def filter(arr:Array[Int]):Array[Int]={
    for (i <- arr if i % 2 != 0) yield i
  }
代码语言:javascript
复制
List(1, 7, 9)

规则是可以的,通过观察,发现 if i % 2 != 0 就是我们想要的规则;于是使用高阶函数进行改进。

需要键元素传给函数,然后在函数中判断是否是需要的元素;所以传入的参数是Int 返回的是Boolean

代码语言:javascript
复制
func:Int=>Boolean
代码语言:javascript
复制
  def main(args: Array[String]): Unit = {
    val arr=Array[Int](1,4,2,7,9,10)

    println(filter(arr,(i)=>{i%2!=0}).toList)
  }

  /**
   * 过滤
   * @param arr
   * @return
   */
  def filter(arr:Array[Int],func:Int=>Boolean):Array[Int]={
    for (i <- arr if func(i)) yield i
  }
代码语言:javascript
复制
List(1, 7, 9)

案例二:获取集合中所有的偶数

代码语言:javascript
复制
  def main(args: Array[String]): Unit = {
    val arr=Array[Int](1,4,2,7,9,10)

    println(filter(arr,i=>i%2==0).toList)
  }

  /**
   * 过滤
   * @param arr
   * @return
   */
  def filter(arr:Array[Int],func:Int=>Boolean):Array[Int]={
    for (i <- arr if func(i)) yield i
  }
代码语言:javascript
复制
List(4, 2, 10)

案例三:获取结合中是3的倍数

代码语言:javascript
复制
  def main(args: Array[String]): Unit = {
    val arr=Array[Int](1,4,2,7,9,10)

    println(filter(arr,_%3==0).toList)
  }

  /**
   * 过滤
   * @param arr
   * @return
   */
  def filter(arr:Array[Int],func:Int=>Boolean):Array[Int]={
    for (i <- arr if func(i)) yield i
  }
代码语言:javascript
复制
List(9)
  1. 对数组中元素按照指定规则分组 数据: Array[String]("zhangsan man beijing","lisi woman shenzhen","zhaoliu man shenzhen") 规则: 按照规则进行分组 通过上面两个案例的套路;应该知道该如何编写程序,按照规则进行分组,比如按照性别地区等。

案例一:根据性别分组

代码语言:javascript
复制
 def main(args: Array[String]): Unit = {
    val arr=Array[String]("zhangsan man beijing","lisi woman shenzhen","zhaoliu man shenzhen")

    //根据 性别分组
    println(group(arr,s=>{s.split(" ")(1)}))
  }

  /**
   * 分组
   * @param arr
   * @param func
   * @return
   */
  def group(arr:Array[String],func:String=>String):util.HashMap[String,util.ArrayList[String]]={
    // 用于存放 分组数据
    val map=new util.HashMap[String,util.ArrayList[String]]()
    // 分组;具体按照什么来分组,有func 指定。
    // 只需要将 每个元素传给 func 即可。

    var list:util.ArrayList[String]=null
    for(e <-arr ;key=func(e)){
      // 校验key是否存在,
      if(map.containsKey(key)){
        list=map.get(key)
      }else{
        // 若key不存在,需要创建 集合
        list=new util.ArrayList[String]
      }
      list.add(e)
      // 装回map中
      map.put(key,list);
    }
    map
  }
代码语言:javascript
复制
{woman=[lisi woman shenzhen], man=[zhangsan man beijing, zhaoliu man shenzhen]}

案例一:根据地区分组

代码语言:javascript
复制
def main(args: Array[String]): Unit = {
    val arr=Array[String]("zhangsan man beijing","lisi woman shenzhen","zhaoliu man shenzhen")

    //根据 地区分组
    println(group(arr,s=>{s.split(" ")(2)}))
  }

  /**
   * 分组
   * @param arr
   * @param func
   * @return
   */
  def group(arr:Array[String],func:String=>String):util.HashMap[String,util.ArrayList[String]]={
    // 用于存放 分组数据
    val map=new util.HashMap[String,util.ArrayList[String]]()
    // 分组;具体按照什么来分组,有func 指定。
    // 只需要将 每个元素传给 func 即可。

    var list:util.ArrayList[String]=null
    for(e <-arr ;key=func(e)){
      // 校验key是否存在,
      if(map.containsKey(key)){
        list=map.get(key)
      }else{
        // 若key不存在,需要创建 集合
        list=new util.ArrayList[String]
      }
      list.add(e)
      // 装回map中
      map.put(key,list);
    }
    map
  }
代码语言:javascript
复制
{shenzhen=[lisi woman shenzhen, zhaoliu man shenzhen], beijing=[zhangsan man beijing]}

代码简写:

代码语言:javascript
复制
    //根据 性别分组
    println(group(arr,_.split(" ")(2)))
  1. 对数组中的所有元素按照指定规则聚合 数据: Array[Double](1,4,2,7,9,10) 规则: 求总和,求乘积,求差等。

案例一:求出集合中的总和

代码语言:javascript
复制
  def main(args: Array[String]): Unit = {
    val arr=Array[Double](1,4,2,7,9,10)
    println(reduce(arr,(x,y)=>{x+y}))
  }

  def reduce(array: Array[Double],func:(Double,Double)=>Double): Double ={
    var tmp=array(0)
    for(i <- 1 until array.length){
      tmp=func(tmp,array(i))
    }
    tmp
  }
代码语言:javascript
复制
33.0

案例二:求出集合中的积

代码语言:javascript
复制
  def main(args: Array[String]): Unit = {
    val arr=Array[Double](1,4,2,7,9,10)
    println(reduce(arr,(x,y)=>x*y))

  }

  def reduce(array: Array[Double],func:(Double,Double)=>Double): Double ={

    var tmp=array(0)
    for(i <- 1 until array.length){
      tmp=func(tmp,array(i))
    }
    tmp
  }
代码语言:javascript
复制
5040.0

案例二:求出集合中的差

代码语言:javascript
复制
  def main(args: Array[String]): Unit = {
    val arr=Array[Double](1,4,2,7,9,10)
    println(reduce(arr,_-_))

  }

  def reduce(array: Array[Double],func:(Double,Double)=>Double): Double ={
    var tmp=array(0)
    for(i <- 1 until array.length){
      tmp=func(tmp,array(i))
    }
    tmp
  }
代码语言:javascript
复制
-31.0
  1. 根据指定规则获取数组中的最大值 数据: Array[String]("zhangsan 20 3000","lisi 18 4500","zhaoliu 33 3500") 规则: 根据不同的规则,求出最大值,如按照,年纪,薪资等。

案例一:按照获取年龄最大的用户

代码语言:javascript
复制
def main(args: Array[String]): Unit = {
    val arr=Array[String]("zhangsan 20 3000","lisi 18 4500","zhaoliu 33 3500")
    // 获取年龄最大的用户信息
    println(max(arr,s=>s.split(" ")(1).toInt))

  }


  /**
   * 获取最大值
   * @param array
   * @param func
   * @return
   */
  def max(array: Array[String],func:String=>Int):String={

    // 用于记录最大值元素的下标。
    var index=0

    for(i <- 1 until array.length){
      // 进行比较,记录最大值的元素下标
      if(func(array(index)) < func(array(i))){
        index=i
      }
    }
    array(index)
  }

案例二:获取薪资最高的用户信息

代码语言:javascript
复制
  def main(args: Array[String]): Unit = {
    val arr=Array[String]("zhangsan 20 3000","lisi 18 4500","zhaoliu 33 3500")
    // 获取年龄最大的用户信息
    println(max(arr,_.split(" ")(2).toInt))

  }


  /**
   * 获取最大值
   * @param array
   * @param func
   * @return
   */
  def max(array: Array[String],func:String=>Int):String={

    // 用于记录最大值元素的下标。
    var index=0

    for(i <- 1 until array.length){
      // 进行比较,记录最大值的元素下标
      if(func(array(index)) < func(array(i))){
        index=i
      }
    }

    array(index)
  }
代码语言:javascript
复制
lisi 18 4500

代码优化:

代码语言:javascript
复制
  def max(array: Array[String],func:String=>Int):String={

    // 用于记录最大值元素的下标。
    var index=0
    for(i <- 1 until array.length;tmp=func(array(i)) if func(array(index))<tmp) index=i
    array(index)
  }

之后的很多都没做说明,累了,脑袋优点晕,以后再说吧。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • 高阶函数简化(调用时简化)
  • '_'的限制场景
  • 案例实战
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档