簡體   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