偏函数 PartialFunction

偏函数(PartialFunction)

我们来讨论偏函数(PartialFunction,http://bit.ly/1yMpzEP)的性质。偏函数之所以“偏”,原因在于它们并不处理所有可能的输入,而只处理那些能与至少一个 case 语句匹配的输入(

将包在大括号内的一组case语句封装为函数,我们称之为偏函数,它只对会作用于指定类型的参数或指定范围值的参数实施计算,超出范围的值会忽略.

)。
  • 在偏函数中只能使用 case 语句,而整个函数必须用花括号包围。这与普通的函数字面量不 同,普通函数字面量可以用花括号,也可以用圆括号包围。
  • 如果偏函数被调用,而函数的输入却与所有语句都不匹配,系统就会抛出一个 MatchError(http://www.scala-lang.org/api/current/#scala.MatchError)运行时错误。
  • 我们可以用 isDefineAt 方法测试特定输入是否与偏函数匹配,这样偏函数就可以避免抛出 MatchError 错误了。
  • 偏函数可以如此“链式”连接:pf1 orElse pf2 orElse pf3…。如果 pf1 不匹配,就会尝 试 pf2,接着是 pf3,以此类推。如果以上偏函数都不匹配,才会抛出 MatchError。
以下实例可以展示上述规则:
 1 // src/main/scala/progscala2/typelessdomore/partial-functions.sc
 2 val pf1: PartialFunction[Any,String] = { case s:String => "YES" } //
 3 val pf2: PartialFunction[Any,String] = { case d:Double => "YES" } //
 4 val pf = pf1 orElse pf2 //
 5 def tryPF(x: Any, f: PartialFunction[Any,String]): String = //
 6 try { f(x).toString } catch { case _: MatchError => "ERROR!" }
 7 def d(x: Any, f: PartialFunction[Any,String]) = //
 8 f.isDefinedAt(x).toString
 9 println(" | pf1 - String | pf2 - Double | pf - All") //
10 println("x | def? | pf1(x) | def? | pf2(x) | def? | pf(x)")
11 println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
12 List("str", 3.14, 10) foreach { x =>
13 printf("%-5s | %-5s | %-6s | %-5s | %-6s | %-5s | %-6s\n", x.toString,
14 d(x,pf1), tryPF(x,pf1), d(x,pf2), tryPF(x,pf2), d(x,pf), tryPF(x,pf))
15 }
  1. ➊ 只匹配字符串的偏函数。
  2. ➋ 只匹配 Double 数字的偏函数。
  3. ➌ 将这两个函数结合,得到一个新的偏函数:既能匹配字符串,又能匹配 Double 数字。
  4. ➍ 辅助函数:用于 try 一个偏函数,然后将可能产生的 MatchError 异常捕捉到。无论是否捕获异常,函数均返回一个字符串。
  5. ➎ 辅助函数:使用了 isDefineAt,返回值为字符串。
  6. ➏ 使用了多个偏函数的链式组合,并将结果以表格的形式打印出来。
其他代码对这 3 个偏函数输入不同的值,先调用 isDefineAt(结果显示在输出表中的 def?这一列),然后再尝试调用偏函数本身。输出为:
未输入字符串时,pf1 将会失败;未输入 Double 数字时,pf2 会失败;如果给出整数,这两个函数均失败。组合后的函数 pf 对于字符串或者 Double 数字的输入均成功,但输入整数时仍将失败 。
 
补充:
  1. 使用构建特质的实现类(使用的方式是PartialFunction的匿名子类)
  2. PartialFunction 是个特质(看源码) 构建偏函数时,参数形式 [Any, Int]是泛型,第一个表示传入参数类型,第二个 表示返回参数
  3. 当使用偏函数时,会遍历集合的所有元素,编译器执行流程时先执行isDefinedAt()如果为true ,就会执行 apply, 构建一个新的Int 对象返回 执行isDefinedAt() 为false 就过滤掉这个元素,即不构建新的Int对象.
  4. map函数不支持偏函数,因为map底层的机制就是所有循环遍历,无法过滤处理原来集合的元素
  5. collect函数支持偏函数
 
posted @ 2022-07-31 11:32  linbo.yang  阅读(254)  评论(0编辑  收藏  举报