PartialFunction 设计效率低下吗?

发布于 2024-09-29 15:09:51 字数 969 浏览 0 评论 0原文

这是我想知道有一段时间的事情。我经常看到这种模式:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

白鸥掠海 2024-10-06 15:09:52

关于此的对话 < em>现在在 scala-internals 邮件列表上。 Martin Odersky 建议使用一种新类型:FunctionWithDefault。 Martin 不仅谈到了使用 PartialFunction 的运行时损失,还谈到了编译时损失(类文件膨胀):

首先,我们需要生成两次模式匹配代码,一次在 apply 中,然后在 isDefinedAt 中再次生成。其次,我们还需要执行两次代码,首先测试该功能是否适用,然后再实际应用。

您的问题的答案本质上是“是”,并且这种行为(PartialFunction)也不会因向后兼容性问题而改变(例如,如果isDefinedAt是有副作用的)。

提议的新类型 FunctionWithDefault 没有 isDefinedAt 并有一个方法:

trait FunctionWithDefault[-I, +O] {
  def applyOrElse[OO >: O](i : I, default : I => OO) : OO
}

其作用有点像 OptiongetOrElse > 方法。

我不得不说,像往常一样,我无法想象在绝大多数情况下这种低效率会造成任何类型的性能问题。

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 using PartialFunction:

First, we need to generate the pattern matching code twice, once in the apply and then again in the isDefinedAt. Second, we also need to execute the code twice, first to test whether the function is applicable, and then to actually apply it.

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 the isDefinedAt is side-effecting).

The new type being proposed, FunctionWithDefault has no isDefinedAt and has a method:

trait FunctionWithDefault[-I, +O] {
  def applyOrElse[OO >: O](i : I, default : I => OO) : OO
}

which acts a bit like Options getOrElse 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文