scala之函数式编程

杀马特。学长 韩版系。学妹 提交于 2019-11-27 15:10:53

1、偏函数

① 基本介绍

对符合某个条件,而不是所有情况进行逻辑操作时,使用偏函数是一个不错的选择

② 示例

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

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

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

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

      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)
  }
}

③ 总结

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

  2. PartialFunction 是个特质(看源码)

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

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

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

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

  7. collect函数支持偏函数

2、作为参数的函数

① 基本介绍

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

② 示例

//说明
def plus(x: Int) = 3 + x
//说明
val result1 = Array(1, 2, 3, 4).map(plus(_))
println(result1.mkString(","))

③ 小结

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

3、匿名函数

① 基本介绍

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

② 示例

object AnonFun01 {
  def main(args: Array[String]): Unit = {
    //说明
    //1. 匿名函数  (x: Double) => 3 * x
    //3. 匿名函数没有函数名 ,(x: Double) 形参部分
    //4. => 3 * x :  3 * x 就是函数体
    //5. 匿名函数的返回值使用类型推导即可
    //6. triple 是一个代表函数的变量,通过它可以实现调用匿名函数的调用
    val triple = (x: Double) => 3 * x
    println(triple(3) + "triple 的类型=" + triple)

  }
}

③ 实践

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

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

    val f1 = (n1:Int,n2:Int) => {
      println("匿名函数被调用")
      n1 + n2
    }

    println(f1(10,20) + "f1的类型=" + f1)

  }
}

4、高阶函数

① 基本介绍

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

② 基本使用

//test 就是一个高阶函数,它可以接收f: Double => Double 
def test(f: Double => Double, n1: Double) = {
  f(n1)
}
//sum 是接收一个Double,返回一个Double
def sum(d: Double): Double = {
  d + d
}
val res = test(sum, 6.0)

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

object HigherOrder01 {
  def main(args: Array[String]): Unit = {
    val f1 = minusxy(10)
    println("f1=" + f1) // function1
    println(f1(1)) // ? 9
    println(f1(2)) // ? 8
    println(minusxy(20)(9))  // 20 - 9  = 11

    //f1 就是  (y :int ) = { x - y}
  }

  //高阶函数,可以返回一个函数类型
  //1. minusxy 返回的类型是函数 (y: Int) => x - y
  //2. 返回的匿名函数,使用到本身函数的外部变量 x, 这时匿名函数和x 构成一个闭包
  //3. //匿名函数
  def minusxy(x: Int) = (y: Int) => {x - y}

}

④ 小结

def minusxy(x: Int) = (y: Int) => x - y
1) 函数名为 minusxy
2) 该函数返回一个匿名函数
        (y: Int) => x -y
     
说明val result3 = minusxy(3)(5)

1) minusxy(3)执行minusxy(x: Int)得到 (y: Int) => x - y 这个匿名函
2) minusxy(3)(5)执行 (y: Int) => x - y 这个匿名函数
3) 也可以分步执行: val f1 = minusxy(3);   val res = f1(90)

5、参数类型推断

① 基本介绍

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

② 参数类型推断说明

1) 参数类型是可以推断时,可以省略参数类型
2) 当传入的函数,只有单个参数时,可以省去括号
3) 如果变量只在=>右边只出现一次,可以用_来代替

③ 示例

object TypeInfer {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4)

    //正常的写法,直接给map传入一个匿名函数
    println(list.map((x:Int)=>x + 1)) //(2,3,4,5)


    //简写1,因为编译器可以推断出 List的元素是Int,因此Int可以省略
    println(list.map((x)=>x + 1))//(2,3,4,5)

    //简写2, 因为匿名函数的形参只有一个参数,因此省略()
    println(list.map(x=>x + 1))//(2,3,4,5)

    //简写3 因为我们x,在=》只出现了一次,因此可以使用_来替代,同时将形参部分拿掉。
    println(list.map(_ + 1))//(2,3,4,5)

    //使用简化,完成对list的求和
    println("list的和=" + list.reduceLeft(_+_)) // 10

  }
}

6、闭包

① 基本介绍

基本介绍:闭包就是一个函数和与其相关的引用环境(变量)组合的一个整体(实体)

② 示例

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

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

③ 实践

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

  1. 编写一个函数 makeSuffix(suffix: String) 可以接收一个文件后缀名(比如.jpg),并返回一个闭

  2. 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg) ,则返回 文件名.jpg , 如果已经有.jpg后缀,则返回原文件名。

  3. 要求使用闭包的方式完成

  4. String.endsWith(xx)

object Demo3 {

  def main(args: Array[String]): Unit = {
    println(makeSuffix(".jpg")("zs"))
  }

  def makeSuffix(suffix:String)={
    (fileName :String) => {
      if (fileName .endsWith(suffix)){
        fileName
      } else {
        fileName + suffix
      }
    }
  }

}

7、柯里化

① 基本说明

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

② 示例

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

使用柯里化完成

//常规方式
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))

③ 实践

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

  1. 全部转大写(或小写)

  2. 比较是否相等

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

使用函数柯里化的思想来任务

object CurryDemo02 {
  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(_)))



  }
}

8、控制抽象

① 基本介绍

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

  1. 参数是函数

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

② 示例

object AbstractControl {
  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("干完咯!~~~")
    }

  }
}

③ 示例二

实现类似while的until函数

object AbstractControl2 {
  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 )
    }

  }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!