scala函数式编程 之偏函数(partial function)的简单使用

什么是偏函数(partial function)

官方文档的定义:
在这里插入图片描述

所谓偏函数(也叫部分函数)与完全函数想对应,普通的方法都是完全函数,即 f(i:Int) = xxx 是将所有Int类型作为参数的,是对整个Int集的映射;而偏函数则是对部分数据的映射,是一种不为每个可能的输入值都提供答案的函数。 它仅提供自己接受数据的答案,并定义了它可以处理的数据。 在Scala中,还可以查询部分函数以确定它是否可以处理特定值。

为什么要使用偏函数有什么好处

1 在完全函数中我们定义一个除法的函数。

val divide = (x: Int) => 42 / x

2 当我们输入 0 的时候将会出现问题

scala> divide(0)
java.lang.ArithmeticException: / by zero

这时候我们通常使用一些异常的处理来解决这个问题
Scala允许将除法功能定义为PartialFunction。 这样做时,还明确声明在输入参数不为零时定义了该函数:

val divide = new PartialFunction[Int, Int] {
    def apply(x: Int) = 42 / x
    def isDefinedAt(x: Int) = x != 0
}

有了 偏函数可以做一些使用前的测试

scala> divide.isDefinedAt(1)
res0: Boolean = true

scala> if (divide.isDefinedAt(1)) divide(1)
res1: AnyVal = 42

scala> divide.isDefinedAt(0)
res2: Boolean = false

在使用偏函数的时候不一定要按照最上面的那样去使用它,也可以写一个简单的用法来实现相同的功能。

val divide2: PartialFunction[Int, Int] = {
    case d: Int if d != 0 => 42 / d
}

同样可以实现上面的功能

scala> divide2.isDefinedAt(0)
res0: Boolean = false

scala> divide2.isDefinedAt(1)
res1: Boolean = true

使用偏函数的好处:
1 可以实现将过滤操作在一个函数里实现。

看韩顺平老师的scala语言核心编程的时候有这样一个例子

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

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

解决方式一 模式匹配

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

解决方式二 map 遍历 + filter 过滤


  def caseDemo ={
    //定义要遍历的列表
    val list = List(1,2,3,4,"hello")
    //先过滤然后 再去map
    println(list.filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int]).map(i => i+1))
  }

在这里插入图片描述

偏函数

      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)

在这里插入图片描述

偏函数的实现原理

PartialFunction是一个trait

在这里插入图片描述

trait PartialFunction[-A, +B] extends (A) => B

可以将=>符号视为一个转换器,在这种情况下,可以将(A)=> B解释为将类型A转换为结果类型B的函数。

Scala中的map与collect

在看scala 偏函数的时候其中的案例需要用到collect方法。不仅是要在工具箱中使用其他工具,还因为它们已在某些库(包括Scala集合库)的API中使用。

List(1, 3, 5, "seven") map { case i: Int => i + 1 } //won't work
//scala.MatchError: seven (of class java.lang.String)
List(1, 3, 5, "seven") collect { case i: Int => i + 1 } //it works

scala 中的map 和 collect 的区别

def map[B](f: (A) ⇒ B): List[B]
def collect[B](pf: PartialFunction[A, B]): List[B]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章