13. Scala函数式编程(高级部分)

13.1 偏函数(partial function)

  13.1.1 需求 -> 思考

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

        1) 将集合list中的所有数字+1,并返回一个新的集合

        2) 要求忽略掉非数字的元素,即返回的新的集合形式为(2,3,4,5)

  13.1.2 解决方式-filter+map返回新的集合,引出偏函数

  13.1.3 解决方式-模式匹配

object boke_demo01 {

  def main(args: Array[String]): Unit = {
    //思路1 filter + map 方式解决
    //虽然可以解决问题,但是麻烦.

    val list = List(1, 2, 3, 4, "hello")
    // 先过滤,再map
    println(list.filter(f1).map(f3).map(f2))

    //思路2-模式匹配
    //小结:虽然使用模式匹配比较简单,但是不够完美
    val list2 = list.map(addOne2)
    println("list2=" + list2)

  }

  //模式匹配
  def addOne2(i: Any): Any = {
    i match {
      case x: Int => x + 1
      case _ =>
    }
  }

  def f1(n: Any): Boolean = {
    n.isInstanceOf[Int]
  }

  def f2(n: Int): Int = {
    n + 1
  }

  //将Any->Int [map]
  def f3(n: Any): Int = {
    n.asInstanceOf[Int]
  }
}

13.1.4 偏函数快速入门

    -使用偏函数解决前面的问题

    -案例演示

object boke_demo01 {

  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] {

      override def isDefinedAt(x: Any) = {
        println("x=" + x)
        x.isInstanceOf[Int]
      }

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

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

  13.1.5 偏函数的小结

      1) 使用构建特质的实现类(使用的方式是PartialFunction的匿名子类)

      2) PartialFunction是个特质

      3) 构建偏函数时,参数形式[Any,Int]是泛型,第一个表示参数类型,第二个表示返回参数

      4) 当使用偏函数时,会遍历集合的所有元素,编译器执行流程时先执行isDefinedAt(),如果为true,就会执行apply,构建一个新的对象返回

      5) 执行isDefinedAt()为false就过滤掉这个元素,即不构建新的Int对象

      6) map函数不支持偏函数,因为map底层的机制就是所有循环遍历,无法过滤处理原来集合的元素

      7) collect函数支持偏函数

  13.1.6 偏函数的简写形式

object boke_demo01 {

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

    //可以将前面的案例的偏函数简写
    def partialFun: PartialFunction[Any, Int] = {
      //简写成case 语句
      case i: Int => i + 1
      case j: Double => (j * 2).toInt
    }

    val list = List(1, 2, 3, 4, 1.2, 2.4, 1.9f, "hello")
    val list2 = list.collect(partialFun)
    println("list2=" + list2)

    //第二种简写形式
    val list3 = list.collect {
      case i: Int => i + 1
      case j: Double => (j * 2).toInt
      case k: Float => (k * 3).toInt
    }
    println("list3=" + list3) // (2,3,4,5)

  }
}

13.2 作为参数的函数

  13.2.1 基本介绍

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

  13.2.2 应用案例

object boke_demo01 {

  def main(args: Array[String]): Unit = {
    def plus(x: Int) = 3 + x

    //说明
    val result = Array(1, 2, 3, 4).map(plus(_))
    println(result.mkString(",")) //4,5,6,7

    //说明
    //1. 在scala中,函数也是有类型,比如plus就是 <function1>
    println("puls的函数类型function1" + (plus _))

  }
}

  13.2.3 对案例演示的小结

      1) map(plus(_))中的plus(_)就是将plus这个函数当作一个参数传给了map,_这里代表从集合中遍历出来的一个元素

      2) plus(_)这里也可以写成plus表示对Array(1,2,3,4)遍历,将每次遍历的元素传给plus的x

      3) 进行 3+x 运算后,返回新的Int,并加入到新的集合result中

      4) def map[B,That](f:A=>B)的声明中的 f:A=>B 的一个函数

13.3 匿名函数

  13.3.1 基本介绍

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

  13.3.2 应用案例

object boke_demo01 {

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

  }
}

13.4 高阶函数

  13.4.1 基本介绍

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

  13.4.2 高阶函数基本使用

object boke_demo01 {

  def main(args: Array[String]): Unit = {
    def test(f: Double => Double, f2: Double => Int, n1: Double) = {
      f(f2(n1)) // f(0)
    }

    //sum 是接收一个Double,返回一个Double
    def sum(d: Double): Double = {
      d + d
    }

    def mod(d: Double): Int = {
      d.toInt % 2
    }

    val res = test(sum, mod, 5.0) //
    println("res=" + res) // 2.0

  }
}

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

object boke_demo01 {

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

    //说明
    //1. minusxy是高阶函数,因为它返回匿名函数
    //2. 返回的匿名函数 (y: Int) => x - y
    //3. 返回的匿名函数可以使用变量接收

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

    //分步执行
    //f1 就是 (y: Int) => 3 - y
    val f1 = minusxy(3)
    println("f1的类型=" + f1)
    println(f1(1)) //  2
    println(f1(9)) //  -6

    //也可以一步到位的调用
    println(minusxy(4)(9)) // -5

  }
}

13.5 参数(类型)推断

  13.5.1 基本介绍

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

  13.5.2 参数类型推断写法说明

      1) 参数类型是可以推断时,可以省略参数类型

      2) 当传入的函数,只有单个参数时,可以省去括号

      3) 如果变量只在=>右边只出现一次,可以用_来代替

  13.5.3 应用案例

object boke_demo01 {

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

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

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

    val res = list.reduce(_ + _)

  }

  def f1(n1: Int, n2: Int): Int = {
    n1 + n2
  }
}

13.6 闭包

  13.6.1 基本介绍

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

  13.6.2 案例演示1

object boke_demo01 {

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

    //1.用等价理解方式改写 2.对象属性理解
    def minusxy(x: Int) = (y: Int) => x - y
    //f 函数就是闭包.
    val f = minusxy(20)
    println("f(1)=" + f(1)) // 19
    println("f(2)=" + f(2)) // 18

  }
}

      -对上述案例演示的小结和说明

        1) (y: Int) => x - y 返回的是一个匿名函数,因为该函数引用到函数外的x,那么该函数和x整体形成一个闭包。如:这里val f = minusxy(20)的f函数就是闭包

        2) 可以这样理解,返回函数是一个对象,而x就是该对象的一个字段,它们共同形成一个闭包

        3) 当多次调用f时(可以理解多次调用闭包),发现使用的是同一个x,所以x不变

        4) 在使用闭包时,主要搞清楚返回函数引用了函数外的哪些变量,因为它们会组合成一个整体(实体),形成一个闭包

  13.6.3 案例演示2

object boke_demo01 {

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

    /*
    请编写一个程序,具体要求如下
    1.编写一个函数 makeSuffix(suffix: String) 可以接收一个文件后缀名(比如.jpg),并返回一个闭包
     2.调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg) ,则返回 文件名.jpg , 如果已经有.jpg后缀,则返回原文件名。
     比如 文件名 是 dog =>dog.jpg
     比如  文件名 是 cat.jpg => cat.jpg
    3.要求使用闭包的方式完成
      提示:String.endsWith(xx)

     */
    //使用并测试
    val f = makeSuffix(".jpg")
    println(f("dog.jpg")) // dog.jpg
    println(f("cat")) // cat.jpg

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

13.7 函数柯里化(curry)

  13.7.1 基本介绍

      1) 函数编程中,接受多个参数的函数都可以转化为接受单个参数的函数,这个转化过程就叫柯里化

      2) 柯里化就是证明了函数只需要一个参数而已

  13.7.2 函数柯里化快速入门

  //编写一个函数,接收两个整数,可以返回两个数的乘积,要求:
  //使用常规的方式完成
  //使用闭包的方式完成
  //使用函数柯里化完成

  def mul(x: Int, y: Int) = x * y
  println(mul(10, 10))

  def mulCurry(x: Int) = (y: Int) => x * y
  println(mulCurry(10)(9))

  def mulCurry2(x: Int)(y:Int) = x * y
  println(mulCurry2(10)(8))

  13.7.3 函数柯里化应用案例

object boke_demo01 {

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

    //这是一个函数,可以接收两个字符串,比较是否相等
    def eq(s1: String, s2: String): Boolean = {
      s1.equals(s2)
    }

    //隐式类
    implicit class TestEq(s: String) {
      //体现了将比较字符串的事情,分解成两个任务完成
      //1. checkEq 完转换大小写
      //2. f函数完成比较任务
      def checkEq(ss: String)(f: (String, String) => Boolean): Boolean = {
        f(s.toLowerCase, ss.toLowerCase)
      }
    }
    val str1 = "hello"
    println(str1.checkEq("HeLLO")(eq))

    //在看一个简写形式
    println(str1.checkEq("HeLLO")(_.equals(_)))

  }
}

13.8 控制抽象

  13.8.1 看一个需求

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

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

  13.8.2 控制抽象基本介绍

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

        1) 参数是函数

        2) 函数参数没有输入值也没有返回值

      -控制抽象应用案例(使用控制抽象实现了while语法)

object boke_demo01 {

  def main(args: Array[String]): Unit = {
    //myRunInThread 就是一个抽象控制
    //是没有输入,也没有输出的函数 f1: () => Unit
    def myRunInThread(f1: () => Unit) = {
      new Thread {
        override def run(): Unit = {
          f1() //只写了 f1
        }
      }.start()
    }

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

    }

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

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

  }
}

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

object boke_demo01 {

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

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

    }

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

  }
}

原文地址:https://www.cnblogs.com/zhanghuicheng/p/10876998.html

时间: 2024-11-05 19:25:25

13. Scala函数式编程(高级部分)的相关文章

第3课 Scala函数式编程彻底精通及Spark源码阅读笔记

本课内容: 1:scala中函数式编程彻底详解 2:Spark源码中的scala函数式编程 3:案例和作业 函数式编程开始: def fun1(name: String){ println(name) } //将函数名赋值给一个变量,那么这个变量就是一个函数了. val fun1_v = fun1_ 访问 fun1_v("Scala") 结果:Scala 匿名函数:参数名称用 => 指向函数体 val fun2=(content: String) => println(co

Scala函数式编程进阶

1 package com.dtspark.scala.basics 2 3 /** 4 * 函数式编程进阶: 5 * 1,函数和变量一样作为Scala语言的一等公民,函数可以直接赋值给变量: 6 * 2, 函数更长用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称,但是如果你要使用的话,一般会把这个匿名函数赋值给一个变量(其实是val常量),Spark源码中大量存在这种语法,必须掌握: 7 * 3, 函数可以作为参数直接传递给函数,这极大的简化的编程的语法,为什么这

Scala函数式编程设计原理 第一课 编程范式(Programming Paradigms)

我使用Scala有一两年的时间了,这门语言仿佛有一种魔力,让人用过就不想放手.Scala给我的整个程序生涯带来了非常深刻的影响,让我学会了函数式编程,让我知道了世界上居然还有这么一种优雅.高效.强大的语言. Scala在国外已经非常流行,但是不知为何,在国内总是不温不火,在此,我特别想为Scala这门语言在国内的发展做一些事情.不才不敢谈Scala的编程经验,因为要成为Scala大神还有很长的路要走,只好翻译一份Scala视频教程以飨读者,大家发现有误的地方,请多多批评指教. 这个视频的作者是S

Scala函数式编程(四)函数式的数据结构 下

前情提要 Scala函数式编程指南(一) 函数式思想介绍 scala函数式编程(二) scala基础语法介绍 Scala函数式编程(三) scala集合和函数 Scala函数式编程(四)函数式的数据结构 上 1.List代码解析 今天介绍的内容,主要是对上一篇介绍的scala函数式数据结构补充,主要讲代码.可以先看看上一节,主要讲的是函数式的list,Scala函数式编程(四)函数式的数据结构 上.这些代码我都放在我的公众号里面,包括函数式的List以及一个函数式的二叉搜索树,关注公众号:哈尔的

Scala函数式编程(五) 函数式的错误处理

前情提要 Scala函数式编程指南(一) 函数式思想介绍 scala函数式编程(二) scala基础语法介绍 Scala函数式编程(三) scala集合和函数 Scala函数式编程(四)函数式的数据结构 上 Scala函数式编程(四)函数式的数据结构 下 1.面向对象的错误处理 在介绍scala的函数式的错误处理之前,我们要先来介绍一下其他情况下的错误处理方式. 以java为例,常见的错误处理方式不外乎两种,一种是及时捕捉到异常,然后当场进行处理. try{ ... }catch(Excepti

【Scala】Scala函数式编程初探

函数式编程 函数式编程是种编程典范,它将电脑运算视为函数的计算.函数编程语言最重要的基础是 λ 演算(lambda calculus).而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值).和指令式编程相比,函数式编程强调函数的计算比指令的执行重要.和过程化编程相比,函数式编程里,函数的计算可随时调用. 命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机的指令序列

Scala 函数式编程(一) 什么是函数式编程?

为什么我们需要学习函数式编程?或者说函数式编程有什么优势?这个系列中我会用 scala 给你讲述函数式编程中的优势,以及一些函数式的哲学.不懂 scala 也没关系,scala 和 java 是类似的,在每篇的开头我也会先说明这节中用到的 scala 语法. 为什么函数式编程这几年火起来 如 Python 一样,函数式编程(FP,即Functional Programming)也是近几年才逐渐为人们所知,但它并不是一个多么新的概念.它拥有和面向对象编程(OOP)几乎等长的历史.但纵观每件事的脉络

scala函数式编程(二) scala基础语法介绍

上次我们介绍了函数式编程的好处,并使用scala写了一个小小的例子帮助大家理解,从这里开始我将真正开始介绍scala编程的一些内容. 这里会先重点介绍scala的一些语法.当然,这里是假设你有一些java或者python的基础,毕竟大部分人不会将scala当作第一门学习编程的语言. 不过这些语法知识记不住也没关系,本身语法这种东西就应该在使用中被记住.这里写这篇的目的也只是梳理一遍,方便大家对语法有个初步的印象,后面可以随时查询. PS:所使用的版本是scala 2.11.8,那我们开始吧 一.

scala函数式编程

1.作为值的函数 在Scala中,函数和数字一样,可以在变量中存放函数.可以把函数赋值给一个变量,格式为:val foee=fun _(函数名+空格+_)形式 2.匿名函数 在scala中,不需要给每一个函数命名,而是将该匿名函数赋值给一个变量 例如:Array(3,4,5).map((x:Double)=>3*x) //Array(9,12,15) 在这里,我们告诉map方法,将每个元素乘以3 匿名函数一般格式:(参数1,参数2,...)=>{函数体(代码块)} 3.带函数参数的函数 实现接