【问题标题】:Scala, partial functionsScala,部分函数
【发布时间】:2011-08-05 19:11:18
【问题描述】:

除了通过case 语句之外,还有什么方法可以创建PartialFunction

我很好奇,因为我想表达以下内容(scala pseudo ahead!)...

val bi = BigInt(_)
if (bi.isValidInt) bi.intValue

... 作为一个偏函数,并且做

val toInt : PartialFunction[String, Int] = {
    case s if BigInt(s).isValidInt => BigInt(s).intValue
}

似乎是多余的,因为我创建了两次BigInt

【问题讨论】:

  • 我认为除了打字之外的重要问题是:在未定义的情况下我必须返回/抛出什么,以便orElse 等人能够识别它?

标签: scala pattern-matching partialfunction extractor


【解决方案1】:

不确定我是否理解这个问题。但这是我的尝试:为什么不创建一个提取器?

object ValidBigInt {
  def unapply(s: String): Option[Int] = {
    val bi = BigInt(s)
    if (bi.isValidInt) Some(bi.intValue) else None
  }
}

val toInt: PartialFunction[String, Int] = {
  case ValidBigInt(i) => i
}

另一种选择是(这可能会回答关于是否可以创建 PartialFunction 而不是使用 case 文字的问题):

val toInt = new PartialFunction[String, Int] {
  def isDefinedAt(s: String) = BigInt(s).isValidInt
  def apply(s: String) = BigInt(s).intValue
}

但是由于偏函数的思想是它只是部分定义的,最后你还是会做多余的事情——你需要创建一个大的 int 来测试它是否有效,然后在你创建的函数应用程序中又是大整数……

我在Github 看到了一个项目,它试图通过缓存isDefinedAt 的结果来解决这个问题。如果你去基准测试,你会发现它比默认的 Scala 实现要慢:)

因此,如果您想解决 isDefinedAtapply 的双重性质,您应该直接使用提供 Option[Int] 作为结果的(完整)函数。

【讨论】:

  • 我想要一个部分函数的原因是我将它附加在^? 解析器组合器之后。我喜欢你的想法unapply。从来没用过。谢谢!
  • 实际上,在合适的条件下,偏函数的缓存变体更快;他需要手动进行一些优化,但我很震惊 JIT 不这样做。
【解决方案2】:

如果您愿意,可以写出PartialFunction“速写”:

object pf extends PartialFunction[Int,String] {
  def isDefinedAt(in: Int) = in % 2 == 0

  def apply(in: Int) = {
    if (in % 2 == 0) 
      "even" 
    else 
      throw new MatchError(in + " is odd")
}

【讨论】:

  • 所以MatchError 表示不确定?
  • 好吧,你可以在这里做任何你想做的事情,但是 PartialFunction 文字代码生成基本上只是将模式匹配表达式嵌入到 apply 方法中,这就是模式匹配表达式作为最后一种情况所做的事情(至少从概念上讲——模式匹配器生成的代码读起来非常有趣!)
【解决方案3】:

这个怎么样?

val toInt: PartialFunction[String, Int] = (s: String) => BigInt(s) match {
  case bi if bi.isValidInt => bi.intValue
}

【讨论】:

  • toInt.isDefinedAt("hej") 抛出异常而不是返回 false。 :-/
  • 好吧,它会在预期的BigInteger 构造上抛出NumberFormatException
  • 那么我能看到的唯一解决方案是 Sciss 的解决方案,但是将 unapply 包装在一个 try-catch 块中,异常返回 None
【解决方案4】:

我认为您正在寻找提升/取消提升。 lift 接受一个偏函数并将其转换为一个返回 Option 的函数。 Unlift 接受一个带有一个参数的函数,该参数返回一个选项,并返回一个偏函数。

import scala.util.control.Exception._

scala> def fn(s: String) = catching(classOf[NumberFormatException]) opt {BigInt(s)}
fn: (s: String)Option[scala.math.BigInt]

scala> val fnPf = Function.unlift(fn)
fnPf: PartialFunction[String,scala.math.BigInt] = <function1>

scala> val fn = fnPf.lift
fn: String => Option[scala.math.BigInt] = <function1>

密切相关,您还想查看at this answer 以获取有关 cond 和 condOpt 的信息:

scala> import PartialFunction._
import PartialFunction._

scala> cond("abc") { case "def" => true }
res0: Boolean = false

scala> condOpt("abc") { case x if x.length == 3 => x + x }
res1: Option[java.lang.String] = Some(abcabc)

【讨论】:

    【解决方案5】:

    好的,我知道了

    import java.lang.NumberFormatException
    import scala.util.control.Exception._
    
    val toInt: PartialFunction[String, Int] = {
      catching(classOf[NumberFormatException]) opt BigInt(_) match {
        case Some(bi) if bi.isValidInt => bi.intValue
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-30
      相关资源
      最近更新 更多