В Scala что означает" extends (A => B) " для класса case?
исследуя, как сделать Мемуаризацию в Scala, я нашел некоторый код, который я не grok. Я пытался найти эту конкретную "вещь", но не знаю, как ее назвать; то есть термин, которым к ней обращаться. Кроме того, это не простой поиск с помощью символа, тьфу!
Я видел следующий код для memoization в 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))
}
и это то, что класс case расширяет, что меня смущает,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 {
. Итак, если это было набрано больше дословно, возможно, я бы понял "скачок", сделанный относительно того, как строится памятка.
любая помощь с благодарностью. Спасибо.
5 ответов
A => B
сокращенно Function1[A, B]
, так как Memo
расширяет функцию от A
to B
, наиболее заметно определяется с помощью метода apply(x: A): B
который должен быть определен.
из-за обозначения" infix " вам нужно поставить круглые скобки вокруг типа, т. е. (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
будет instanciated через применение метода Memo
сопутствующий объект, автоматически генерируемый компилятором с 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
выходит 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
})
обратите внимание, что я значительно упростил способ сопоставления шаблонов, но я думал, что начинаю обсуждение unapply
и unapplySeq
было бы не по теме и запутанно.
Я оригинал автор делает memoization таким образом. Вы можете увидеть некоторые примеры использования в том же файле. Он также работает очень хорошо, когда вы хотите запомнить несколько аргументов из-за того, как 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__, так и Николасом Ринаудо.
резюме:
есть много удобных (но также сильно переплетенных) предположений, сделанных компилятором Scala.
- Scala лечит
extends (A => B)
как синонимextends Function1[A, B]
(ScalaDoc для Function1[+T1, - R]) - конкретная реализация унаследованного абстрактного метода Function1
apply(x: A): B
необходимо обеспечить;def apply(x: A): B = cache.getOrElseUpdate(x, f(x))
- Scala предполагает подразумеваемое
match
для блока кода, начинающегося с= Memo {
- Scala передает содержимое между
{}
начато в пункте 3 в качестве параметра конструктора класса Memo case - Scala предполагает подразумеваемый тип между
{}
начато в пункте 3 какPartialFunction[Int, BigInt]
и компилятор использует блок кода "match" в качестве переопределения для метода PartialFunctionapply()
и затем предоставляет дополнительное переопределение для метод частичной функцииisDefinedAt()
.
детали:
первый блок кода, определяющий Memo класса case, может быть написан более подробно как таковой:
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
}
)
}
пришлось добавить lazy
к валу второго блока кода для того чтобы общаться с само-референтной проблемой в строке case n => fibonacci(n-1) + fibonacci(n-2)
.
и наконец, пример использования Фибоначчи:
val x:BigInt = fibonacci(20) //returns 6765 (almost instantly)
еще одно слово об этом extends (A => B)
: the 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
здесь только помогает ID экземпляра fibonacci
другим, что у него есть apply
метод и, таким образом, может выполнять некоторые задания.