很有可能要知道某个函数是否在某个时刻定义,计算其价值的重要部分必须完成。在PartialFunction
中,在实施isDefined
和apply
时,两种方法都必须这样做。怎么办这个常见的工作成本很高?
有可能缓存其结果,希望在isDefined之后调用apply。绝对丑陋。
我经常希望PartialFunction[A,B]
为Function[A, Option[B]]
,这显然是同构的。或许,PartialFunction
中可能有另一种方法,比如applyOption(a: A): Option[B]
。对于一些mixins,实现者可以选择实现isDefined和apply或applyOption。或者所有这些都是安全的,性能明智。在呼叫申请之前测试isDefined
的客户将被鼓励使用applyOption
代替。
但事实并非如此。库中的一些主要方法,其中集合中的collect
需要PartialFunction
。有没有干净(或不那么干净)的方法来避免支付isDefined和apply之间重复的计算?
此外,applyOption(a: A): Option[B]
方法合理吗?在未来的版本中添加它听起来可行吗?它值得吗?
答案 0 :(得分:4)
为什么要缓存这样的问题?在大多数情况下,您有本地计算,因此只要您为缓存编写包装器,就不必担心它。我的实用程序库中有以下代码:
class DroppedFunction[-A,+B](f: A => Option[B]) extends PartialFunction[A,B] {
private[this] var tested = false
private[this] var arg: A = _
private[this] var ans: Option[B] = None
private[this] def cache(a: A) {
if (!tested || a != arg) {
tested = true
arg = a
ans = f(a)
}
}
def isDefinedAt(a: A) = {
cache(a)
ans.isDefined
}
def apply(a: A) = {
cache(a)
ans.get
}
}
class DroppableFunction[A,B](f: A => Option[B]) {
def drop = new DroppedFunction(f)
}
implicit def function_is_droppable[A,B](f: A => Option[B]) = new DroppableFunction(f)
然后如果我有一个昂贵的计算,我会编写一个函数方法A => Option[B]
并执行类似(f _).drop
的操作,以便在collect或whatnot中使用它。 (如果你想内联它,你可以创建一个方法,取A=>Option[B]
并返回一个部分函数。)
(相反的变换 - 从PartialFunction
到A => Option[B]
- 被称为提升,因此“下降”;“unlift”是一个更广泛使用的术语,用于相反的操作。)
答案 1 :(得分:3)
看一下这个帖子Rethinking PartialFunction。你不是唯一一个对此感到疑惑的人。
答案 2 :(得分:2)
这是一个有趣的问题,我会给出2美分。
首先抵制过早优化的冲动。确保部分功能是问题。我对他们在某些情况下的速度感到惊讶。
现在假设有问题,它会从哪里来?
一种选择我试图找到快速失败的方法。将模式匹配分解为图层,然后链接部分函数。这样你就可以提早失败。还提取重复的子匹配。例如:
让我们假设OddEvenList是一个将列表分成奇数列表和偶数列表的提取器:
var pf1: PartialFuntion[List[Int],R] = {
case OddEvenList(1::ors, 2::ers) =>
case OddEvenList(3::ors, 4::ors) =>
}
分为两部分,一部分与分裂相匹配,然后一部分试图匹配重复结果(以避免重复计算。但这可能需要一些重新设计
var pf2: PartialFunction[(List[Int],List[Int],R) = {
case (1 :: ors, 2 :: ers) => R1
case (3 :: ors, 4 :: ors) => R2
}
var pf1: PartialFuntion[List[Int],R] = {
case OddEvenList(ors, ers) if(pf2.isDefinedAt(ors,ers) => pf2(ors,ers)
}
我在逐步阅读难以格式化的XML文件时使用了这个。
另一个选择是使用andThen
撰写部分功能。虽然这里的快速测试结果表明只有第一个实际上是测试。
答案 3 :(得分:0)
部分函数内部的缓存机制绝对没有问题,如果:
这样的缓存函数不能从普通的纯部分函数中解除...