我PartialFunction
在Scala中定义了2个版本.
val doubleEvens1: PartialFunction[Int, Int] = { case x if x % 2 == 0 => x * 2 } val doubleEvens2 = new PartialFunction[Int, Int] { override def isDefinedAt(x: Int): Boolean = x % 2 == 0 override def apply(v1: Int): Int = v1 * 2 }
和一份清单:
val list = List(1, 2, 3, 4, 5)
但是他们的行为为未定义的值区分:
// doubleEvens1 println(doubleEvens1.isDefinedAt(3)) // false println(list.collect(doubleEvens1)) // List(4, 8) println(doubleEvens1(3)) // scala.MatchError println(list.map(doubleEvens1)) // scala.MatchError // doubleEvens2 println(doubleEvens2.isDefinedAt(3)) // false println(list.collect(doubleEvens2)) // List(4, 8) println(doubleEvens2(3)) // 6 println(list.map(doubleEvens2)) // List(2, 4, 6, 8, 10)
我想doubleEvens1
应该是合理的,因为它符合数学定义.但是doubleEvens2
按目的设计的行为是什么?或者我在代码片段中遗漏了什么?
如果您实现自己的apply
方法而不进行检查isDefinedAt
,那么显然您可以使用任何输入调用该方法,而不会抛出异常.这在文档中明确说明:
调用者有责任在调用
isDefinedAt
之前调用apply
,因为如果isDefinedAt
为false,则不保证apply
会抛出异常来指示错误情况.如果未抛出异常,则评估可能会导致任意值.
这恰好从一个模式匹配制成的部分功能将调用isDefinedAt
本身从它的apply
方法实现,并抛出一个异常.
因此,如果要复制该行为,则必须执行以下操作:
val doubleEvens2 = new PartialFunction[Int, Int] { override def isDefinedAt(x: Int): Boolean = x % 2 == 0 override def apply(x: Int): Int = if (isDefinedAt(x)) x * 2 else throw new MatchError(x.toString) }
当然缺点是isDefined
可能会多次调用某些code(),如本问题所述.