繁体   English   中英

在Scala中,案例类的“扩展(A => B)”是什么意思?

[英]In Scala, what does “extends (A => B)” on a case class mean?

在研究如何在Scala中进行备忘时,我发现了一些我不喜欢的代码。 我试图查找这种特殊的“事物”,但是不知道用什么来称呼它。 即引用它的术语。 此外,使用符号搜索也不容易!

我在这里看到了以下代码可在Scala中进行记忆化:

case class Memo[A,B](f: A => B) extends (A => B) {
  private val cache = mutable.Map.empty[A, B]
  def apply(x: A) = cache getOrElseUpdate (x, f(x))
}

extends (A => B)部分正是使案例类扩展的东西。 首先,发生了什么事? 其次,为什么还需要它? 最后,您怎么称这种继承? 即是否可以使用一些特定的名称或术语来指代它?

接下来,我看到这样计算Fibanocci号码使用备注这里

  val fibonacci: Memo[Int, BigInt] = Memo {
    case 0 => 0
    case 1 => 1
    case n => fibonacci(n-1) + fibonacci(n-2)
  }

我可能没有看到所有正在应用的“简化”。 但是,我不知道val行的结尾= Memo { 因此,如果更详细地输入,也许我会理解备忘录的构造方式正在“飞跃”。

在这方面的任何帮助将不胜感激。 谢谢。

A => BFunction1[A, B]缩写,因此您的Memo将函数从A扩展到B ,最明显的是通过必须定义的apply(x: A): B方法定义的。

由于使用了“中缀”符号,因此需要在类型周围加上括号,即(A => B) 你也可以写

case class Memo[A, B](f: A => B) extends Function1[A, B] ...

要么

case class Memo[A, B](f: Function1[A, B]) extends Function1[A, B] ...

为了完成0_的答案, fibonacci通过Memo的伴随对象的apply方法实例化,由于Memo是case类,因此编译器会自动生成该对象。

这意味着将为您生成以下代码:

object Memo {
  def apply[A, B](f: A => B): Memo[A, B] = new Memo(f)
}

Scala对apply方法有特殊的处理方式:调用它的名称时不需要键入其名称。 以下两个调用严格等效:

Memo((a: Int) => a * 2)

Memo.apply((a: Int) => a * 2)

case块称为模式匹配。 在引擎盖下,它生成一个部分函数-即为其某些输入参数(但不一定是所有输入参数)定义的函数。 我不会详细介绍局部函数(因为这很重要, 是我就该主题写给自己的备忘录),但是从本质上讲,这意味着case块实际上是PartialFunction的一个实例。

如果您PartialFunction该链接,将会看到PartialFunction扩展了Function1-这是Memo.apply的预期参数。

因此,一旦删除(如果是一个单词),那段代码实际上意味着的是:

lazy val fibonacci: Memo[Int, BigInt] = Memo.apply(new PartialFunction[Int, BigInt] {
  override def apply(v: Int): Int =
    if(v == 0)      0
    else if(v == 1) 1
    else            fibonacci(v - 1) + fibonacci(v - 2)

  override isDefinedAt(v: Int) = true
})

请注意,我已经大大简化了模式匹配的处理方式,但我认为在开始讨论关于unapplyunapplySeq是题外话和混乱。

我是以此方式进行记忆的原始作者。 您可以在同一文件中看到一些示例用法。 当您也想记住多个参数时,由于Scala展开元组的方式,它也非常有效:

    /**
     * @return memoized function to calculate C(n,r) 
     * see http://mathworld.wolfram.com/BinomialCoefficient.html
     */
     val c: Memo[(Int, Int), BigInt] = Memo {
        case (_, 0) => 1
        case (n, r) if r > n/2 => c(n, n-r)
        case (n, r) => c(n-1, r-1) + c(n-1, r)
     }
     // note how I can invoke a memoized function on multiple args too
     val x = c(10, 3) 

该答案是0__和Nicolas Rinaudo提供的部分答案的综合。

摘要:

Scala编译器有许多方便的假设(但也是如此)。

  1. Scala将extends (A => B)extends Function1[A, B]同义( Function1 [+ T1,-R]的ScalaDoc
  2. 必须提供Function1继承的抽象方法apply(x: A): B的具体实现; def apply(x: A): B = cache.getOrElseUpdate(x, f(x))
  3. 阶假定一个隐含的match的码块开头= Memo {
  4. Scala将第3项中{}之间的内容作为参数传递给Memo case类构造函数
  5. Scala假定在第3项中的{}之间为隐式类型,为PartialFunction[Int, BigInt] ,并且编译器使用“ match”代码块作为PartialFunction方法的apply()的替代,然后为PartialFunction的方法isDefinedAt()提供其他替代isDefinedAt()

细节:

定义案例类Memo的第一个代码块可以这样写:

case class Memo[A,B](f: A => B) extends Function1[A, B] {    //replaced (A => B) with what it's translated to mean by the Scala compiler
  private val cache = mutable.Map.empty[A, B]
  def apply(x: A): B = cache.getOrElseUpdate(x, f(x))  //concrete implementation of unimplemented method defined in parent class, Function1
}

可以更详细地编写定义val fibanocci的第二个代码块,如下所示:

lazy val fibonacci: Memo[Int, BigInt] = {
  Memo.apply(
    new PartialFunction[Int, BigInt] {
      override def apply(x: Int): BigInt = {
        x match {
          case 0 => 0
          case 1 => 1
          case n => fibonacci(n-1) + fibonacci(n-2)
        }
      }
      override def isDefinedAt(x: Int): Boolean = true
    }
  )
}

为了在行case n => fibonacci(n-1) + fibonacci(n-2)处理自指问题,必须向第二个代码块的val添加lazy

最后,斐波那契的用法示例是:

val x:BigInt = fibonacci(20) //returns 6765 (almost instantly)

关于此extends (A => B)一句话extends (A => B) :这里的extends不是必需的,但是如果要在更高阶的函数或情况下使用Memo的实例,则必须这样做。

没有这种extends (A => B) ,如果仅在方法调用中使用Memo实例fibonacci ,那就完全可以了。

case class Memo[A,B](f: A => B) {
    private val cache = scala.collection.mutable.Map.empty[A, B]
    def apply(x: A):B = cache getOrElseUpdate (x, f(x))
}
val fibonacci: Memo[Int, BigInt] = Memo {
    case 0 => 0
    case 1 => 1
    case n => fibonacci(n-1) + fibonacci(n-2)
}

例如:

Scala> fibonacci(30)
res1: BigInt = 832040

但是,当您想在高阶函数中使用它时,会出现类型不匹配错误。

Scala> Range(1, 10).map(fibonacci)
<console>:11: error: type mismatch;
 found   : Memo[Int,BigInt]
 required: Int => ?
              Range(1, 10).map(fibonacci)
                               ^

因此,这里的extends仅有助于将实例fibonacci标识给其他实例,因为它具有apply方法,因此可以完成一些工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM