PartialFunction 设计效率低下吗?
这是我想知道有一段时间的事情。我经常看到这种模式:
if (pf.isDefinedAt(in)) pf(in)
通过将其分解为两个单独的调用,在 #isDefinedAt 中评估的所有模式也会在 #apply 中评估。例如:
object Ex1 {
def unapply(in: Int) : Option[String] = {
println("Ex1")
if (in == 1) Some("1") else None
}
}
object Ex2 {
def unapply(in: Int) : Option[String] = {
println("Ex2")
if (in == 2) Some("2") else None
}
}
val pf : PartialFunction[Int,String] = {
case Ex1(result) => result
case Ex2(result) => result
}
val in = 2
if (pf.isDefinedAt(in)) pf(in)
Which prints
Ex1
Ex2
Ex1
Ex2
res52: Any = 2
在最坏的情况下,您的模式最后匹配,您在调用 PartialFunction 时已对模式/提取器进行了两次评估。当匹配的自定义提取器不仅仅是简单的类或列表模式匹配时(例如,如果您有一个解析 XML 文档并返回一些值对象的提取器),这可能会变得低效。
PartialFunction#lift 也会遇到同样的双重问题:评估:
scala> pf.lift(2)
Ex1
Ex2
Ex1
Ex2
res55: Option[String] = Some(2)
是否有一种方法可以有条件地调用一个已定义的函数,而无需两次调用所有提取器?
This is something I've wondered about for a while. I see this pattern a lot:
if (pf.isDefinedAt(in)) pf(in)
By breaking this up into two separate calls, all of the patterns that were evaluated in #isDefinedAt are then also evaluated in #apply. For example:
object Ex1 {
def unapply(in: Int) : Option[String] = {
println("Ex1")
if (in == 1) Some("1") else None
}
}
object Ex2 {
def unapply(in: Int) : Option[String] = {
println("Ex2")
if (in == 2) Some("2") else None
}
}
val pf : PartialFunction[Int,String] = {
case Ex1(result) => result
case Ex2(result) => result
}
val in = 2
if (pf.isDefinedAt(in)) pf(in)
Which prints
Ex1
Ex2
Ex1
Ex2
res52: Any = 2
In the worst case, where your pattern matches last, you've evaluated your patterns/extractors twice when calling a PartialFunction. This could become inefficient when matching over custom extractors that did more than just a simple class or list pattern match (for example, if you had an extractor that parsed an XML document and returned some value objects)
PartialFunction#lift suffers from the same double-evaluation:
scala> pf.lift(2)
Ex1
Ex2
Ex1
Ex2
res55: Option[String] = Some(2)
Is there a way to conditionally call a function if it is defined without potentially calling all of your extractors twice?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
有关于此的对话 < em>现在在 scala-internals 邮件列表上。 Martin Odersky 建议使用一种新类型:
FunctionWithDefault
。 Martin 不仅谈到了使用PartialFunction
的运行时损失,还谈到了编译时损失(类文件膨胀):您的问题的答案本质上是“是”,并且这种行为(
PartialFunction
)也不会因向后兼容性问题而改变(例如,如果isDefinedAt
是有副作用的)。提议的新类型
FunctionWithDefault
没有isDefinedAt
并有一个方法:其作用有点像
Option
的getOrElse
> 方法。我不得不说,像往常一样,我无法想象在绝大多数情况下这种低效率会造成任何类型的性能问题。
There is a conversation going on about this right now on the scala-internals mailing list. Martin Odersky has suggested a new type:
FunctionWithDefault
. Martin talks not only of a run-time penalty, but a compile time penalty (of class file bloat) of usingPartialFunction
:The answer to your question is essentially "yes" and and this behaviour (of
PartialFunction
) will not change either due to backwards-compatibility issues (for example, what if theisDefinedAt
is side-effecting).The new type being proposed,
FunctionWithDefault
has noisDefinedAt
and has a method:which acts a bit like
Option
sgetOrElse
method.I have to say that, as usual, I cannot imagine this inefficiency poses any sort of performance problem in the overwhelming majority of cases.