Увидев сигнатуру метода карты в scala как
def map[A, B](l: List[A])(f: A => B): List[B] = ???
мое непрофессиональное понимание [A, B] выше заключается в том, что они сигнализируют методу, что мы будем работать исключительно с универсальными типами A и B в определенном методе.
Теперь я приступаю к реализации метода для последовательного добавления значений из двух списков (в основном zipWith), используя аналогичный шаблон кодирования.
def addCorresponding[Int](l1: List[Int],
l2: List[Int]): List[Int] =
(l1, l2) match {
case (_, Nil) => Nil
case (Nil, _) => Nil
case (x::xs, y::ys) => {
List((x + y)) ++ addCorresponding(xs, ys)
}
}
но получите эту ошибку при добавлении элементов в последнем совпадении: Найдено несоответствие типа: Int Требуется: String
Удаление [Int] из подписи mehtod исправляет эту ошибку.
Я уверен, что в моем понимании универсальных типов и того, когда их добавлять в сигнатуру метода, есть пробелы. Я был бы признателен, если бы кто-нибудь провел меня через это и объяснил, почему метод addCorresponding
с ошибками [Int] не работает, но отлично работает без него?
Также обратите внимание, что когда вы делаете [Int]
, вы создаете параметр типа с именем Int
, который затеняет тип Int, поэтому он не работает, когда вы добавляете это.
Ах, теперь это имеет смысл. Спасибо за Ваш ответ.
Есть очень похожий вопрос: stackoverflow.com/q/65322171/2359227
Кстати, почему вы не используете zip
? Он делает именно то, что вы пытаетесь сделать. scastie.scala-lang.org/yYCapPvmSgWWE467QRh4ZQ
@TomerShetah Спасибо, что поделились ссылками. Я только начал изучать Scala по Красной книге и застрял в реализации zip самостоятельно в соответствии с одним из упражнений.
Общий метод (с параметром типа) будет
def addCorresponding[A](l1: List[A], l2: List[A]): List[A]
или, если списки могут иметь разные типы элементов
def addCorresponding[A, B, C](l1: List[A], l2: List[B]): List[C]
Если вы реализуете метод, который работает с конкретным типом Int
, то ваш метод больше не является универсальным:
def addCorresponding(l1: List[Int], l2: List[Int]): List[Int]
Другими словами, вы применяете конкретный тип Int
здесь к List[_]
вместо параметра абстрактного типа. Если вы пишете
def addCorresponding[Int](l1: List[Int], l2: List[Int]): List[Int]
затем вы «затеняете» конкретный тип Int
параметром абстрактного типа Int
, который имеет то же имя; но технически вы пишете общий метод
def addCorresponding[XXX](l1: List[XXX], l2: List[XXX]): List[XXX]
Если вы хотите, чтобы ваш метод был универсальным, но выполнял определенные операции с элементами типа A
, вам может понадобиться класс типа, например, Numeric
для арифметического добавления элементов.
def addCorresponding[A](l1: List[A],
l2: List[A])(implicit num: Numeric[A]): List[A] =
(l1, l2) match {
case (_, Nil) => Nil
case (Nil, _) => Nil
case (x::xs, y::ys) =>
List((num.plus(x, y))) ++ addCorresponding(xs, ys)
}
Спасибо за подробный ответ и общую реализацию.
Параметр типа — это, по сути, параметр. Например, когда вы делаете
def plusFive(x: Int): Int = x + 5
, что такоеx
? Все, что передается в функцию. Это не1
, и не0
, и даже не5
и не3
, это просто параметр. Таким образом, в первом примереA
иB
являются параметрами для типов, что означает, что сейчас они не являются каким-либо правильным типом, они будут заполнены компилятором при вызове. Во втором примере вы хотите, чтобы оба списка имели тип Int, так как вы хотите суммировать элементы, это то, что вы можете делать только с Ints, поэтому не нужно добавлять параметр типа.