【问题标题】:Why doesn't Scala optimize calls to the same Extractor?为什么 Scala 不优化对同一个 Extractor 的调用?
【发布时间】:2014-12-16 07:17:49
【问题描述】:

以下面的例子为例,为什么提取器被多次调用,而不是临时存储第一次调用的结果并与之匹配。假设unapply 的结果在给定相同字符串的情况下不会改变是不是很合理。

object Name {
  val NameReg = """^(\w+)\s(?:(\w+)\s)?(\w+)$""".r

  def unapply(fullName: String): Option[(String, String, String)] = {
    val NameReg(fname, mname, lname) = fullName
    Some((fname, if (mname == null) "" else mname, lname))
  }
}

"John Smith Doe" match {
  case Name("Jane", _, _) => println("I know you, Jane.")
  case Name(f, "", _) => println(s"Hi ${f}")
  case Name(f, m, _) => println(s"Howdy, ${f} ${m}.")
  case _ => println("Don't know you")
}

【问题讨论】:

  • 一般来说,判断一个函数是否是纯函数是不可能的。例如,参见stackoverflow.com/questions/11620395/side-effects-in-scala。鉴于(我认为)记录了按顺序检查案例,有可能(但风格很差)编写一个依赖副作用的 unapply。
  • @Paul:但是关于如何在匹配语句中使用提取器的规范可以被设计为允许这种优化,即使它们是可观察的关于有副作用的提取器。所以这个问题仍然存在。
  • 是的,但不是这里有两个不同的问题——我相信,问的一个是,为什么编译器今天不优化这个,答案是因为它不能证明unapply 是纯粹的,规范并没有禁止 unapply 有副作用。如果问题是“是否可以更改语言规范以允许这种优化”,那么答案显然是肯定的。但这不是问的问题
  • 加油!关于以“spec sais so”开头和结尾的编程语言设计的问题的答案是一个非常糟糕的答案。为什么规范是这样的?我也想知道!
  • Scala 中的很多东西都是这样的,因为 Scala 最初是作为改进的 Java 设计的,重点是 Java 兼容性。因此,特别是早期的特性被指定为与惯用的 Java “玩得很好”,即具有副作用的代码。 @som-snytt 的回答告诉我们,新特性的指定方式不同,因此规范保持这种方式可能更多是因为兼容性,而不是因为这是当今 Scala 社区的最佳权衡。

标签: scala matching extractor


【解决方案1】:

假设unapply 的结果在给定相同字符串的情况下不会改变是不是很合理。

不幸的是,假设对于(静态)编译器来说还不够好。为了使记忆成为合法的优化,编译器必须证明被记忆的表达式是纯的并且引用透明的。但是,在一般情况下,这相当于解决了停机问题。

当然可以编写一个优化过程,尝试证明某些表达式的纯度并记住它们当且仅当它成功,但这可能比它的价值更麻烦。这样的证明很快就会变得非常困难,因此它们只可能在非常琐碎的表达式上成功,无论如何执行都非常快。

【讨论】:

    【解决方案2】:

    什么是模式匹配?规范说它匹配值的“形状”并将 vars 绑定到它的“组件”。

    在变异领域,你有这样的问题,如果我匹配case class C(var v: V)case C(x) 是否捕获可变字段?好吧,答案是否定的:

    https://issues.scala-lang.org/browse/SI-5158

    规范说(对不起)评估顺序可能会改变,所以它建议不要产生副作用:

    为了提高效率,模式匹配的评估 表达式可能会以文本顺序以外的其他顺序尝试模式。 这可能会通过守卫中的副作用影响评估。

    这与守卫表达式有关,大概是因为提取器是在案例类之后添加的。

    没有特别承诺只评估一次提取器。 (在规范中明确表示。)

    语义只是“按顺序尝试模式”。

    此外,您的正则表达式示例可以简化,因为当正则表达式未应用于其自己的匹配时,不会重新评估。请参阅example in the doc。也就是说,

    Name.NameReg findFirstMatchIn s map {
      case Name("Jane",_,_) =>
    }
    

    在哪里

    object Name { def unapply(m: Regex.Matcher) = ... }
    

    【讨论】:

      猜你喜欢
      • 2013-05-08
      • 2012-01-04
      • 2016-09-22
      • 2013-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-26
      • 2021-07-13
      相关资源
      最近更新 更多