Зачем этому параметру функции нужны двойные скобки в кортеже?
Примечание: я использую скаляк. Пожалуйста, не рекомендуйте использовать sbt.
Я столкнулся со специфической проблемой, которую мог бы решить, но мне интересно, почему это работает именно так, а не так, как я это делал раньше. Вот фрагмент кода:
def multiply[A](r1: Vector[A], r2: Vector[A], multOp: (A,A) => A, sumOp: (A, A) => A) =
r1.zip(r2).map(multOp).reduce(sumOp)
Он не компилируется, что приводит к появлению сообщения об ошибке, например:
Error:(73, 20) type mismatch;
found : (A, A) => A
required: ((A, A)) => ?
r1.zip(r2).map(multOp).reduce(sumOp)
Изменение сниппета на:
def multiply[A](r1: Vector[A], r2: Vector[A], multOp: ((A,A)) => A, sumOp: (A, A) => A) =
r1.zip(r2).map(multOp).reduce(sumOp)
решит проблему.
Обратите внимание, что sumOp
работает только с одной парой скобок.
Зачем?
2 ответа
Метод map
определяется как принятие одного параметра, и (A, A) => A
имеет два Преобразуя два параметра типа A в один параметр, который является кортежем типа (A, A), он компилируется.
(A, A) => A // fails due to two params of type A
((A, A)) => A // works due to one param of type (A, A)
С другой стороны, reduce
определяется как принятие двух параметров одного типа, так что он счастлив sumOp
что соответствует этому описанию.
Вот полные подписи, найденные в TraversableLike
а также TraversableOnce
соответственно:
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That
def reduce[A1 >: A](op: (A1, A1) => A1): A1
РЕДАКТИРОВАТЬ (дополнительная информация):
Причиной этого является тот факт, что reduce
всегда принимает 2-арную функцию (то есть функцию двух параметров), чтобы свести коллекцию к одному значению, многократно применяя эту функцию к результату предыдущего применения и следующему значению. С другой стороны, map
всегда принимает 1-арную функцию и сопоставляет базовое значение с этой функцией. В случае Option
, Future
и т. д. есть только одно базовое значение, в то время как в случае Vector
(как и у вас) их может быть много, поэтому он применяется к каждому элементу коллекции.
В некоторых библиотеках вы можете встретить map2
которая принимает двухпараметрическую функцию. Например, чтобы объединить два Options (фактически, любые аппликативные функторы, но давайте оставим теорию в стороне), вы можете сделать:
// presudocode
Option(1, 2).map2((a, b) => a + b)
что даст вам Option(3)
, Я думаю, что этот механизм был отброшен в пользу более понятного продукта + карта
// presudocode
(Option(1) product Option(2)) map ((a, b) => a + b)
Фактический синтаксис скалаза для приведенной выше строки будет выглядеть так (в Scalaz 7):
(Option(1) |@| Option(2))((a, b) => a + b)
Это одинаково мощные принципы (то, что можно сделать, точно так же, как и другой, не больше, не меньше), поэтому последний обычно предпочтительнее, а иногда предоставляется только один, но да, вы можете встретить map2
время от времени.
Хорошо, это немного дополнительной информации. Так далеко как map
обеспокоен, просто помните, что всегда есть только один входящий параметр и одно выходное значение.
Первый фрагмент будет работать, если вы определите его как:
def multiply[A](r1: Vector[A], r2: Vector[A], multOp: (A,A) => A, sumOp: (A, A) => A) =
(r1, r2).zipped.map(multOp).reduce(sumOp)
map
метод zipped
кортеж принимает функцию с двумя аргументами (A, A) => B
, потому что это ожидаемый шаблон использования.
Этот подход также позволяет избежать создания промежуточного Vector[(A, A)]
,