Методы могут объявляться с несколькими списками параметров.
Пример
Вот пример, определенный для трейта Iterable
из API Scala коллекций:
trait Iterable[A] {
...
def foldLeft[B](z: B)(op: (B, A) => B): B
...
}
trait Iterable[A]:
...
def foldLeft[B](z: B)(op: (B, A) => B): B
...
foldLeft
применяет бинарный оператор op
к начальному значению z
и ко всем остальным элементам коллекции слева направо.
Ниже приведен пример его использования.
Начиная с начального значения 0
, foldLeft
применяет функцию (m, n) => m + n
к каждому элементу списка
и предыдущему накопленному значению.
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val res = numbers.foldLeft(0)((m, n) => m + n)
println(res) // 55
Варианты для использования
Предлагаемые варианты для использования множественных списков параметров включают:
Вывод типа
Исторически сложилось, что в Scala вывод типов происходит по одному списку параметров за раз. Скажем, у вас есть следующий метод:
def foldLeft1[A, B](as: List[A], b0: B, op: (B, A) => B) = ???
Затем при желании вызвать его следующим образом, можно обнаружить, что метод не компилируется:
def notPossible = foldLeft1(numbers, 0, _ + _)
вам нужно будет вызвать его одним из следующих способов:
def firstWay = foldLeft1[Int, Int](numbers, 0, _ + _)
def secondWay = foldLeft1(numbers, 0, (a: Int, b: Int) => a + b)
Это связано с тем, что Scala не может вывести тип функции _ + _
, так как она все еще выводит A
и B
.
Путем перемещения параметра op
в собственный список параметров, A
и B
выводятся в первом списке.
Затем эти предполагаемые типы будут доступны для второго списка параметров
и _ + _
станет соответствовать предполагаемому типу (Int, Int) => Int
.
def foldLeft2[A, B](as: List[A], b0: B)(op: (B, A) => B) = ???
def possible = foldLeft2(numbers, 0)(_ + _)
Последнее определение метода не нуждается в подсказках типа и может вывести все типы своих параметров.
Неявные параметры
Чтоб указать что параметр используется неявно (implicit) необходимо задавать несколько списков параметров. Примером может служить следующее:
def execute(arg: Int)(implicit ec: scala.concurrent.ExecutionContext) = ???
def execute(arg: Int)(using ec: scala.concurrent.ExecutionContext) = ???
Частичное применение
Методы могут объявляться с несколькими списками параметров. При этом когда такой метод вызывается с меньшим количеством списков параметров, это приводит к созданию новой функции, которая ожидает на вход недостающий список параметров. Формально это называется частичное применение.
Например,
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val numberFunc = numbers.foldLeft(List[Int]()) _
val squares = numberFunc((xs, x) => xs :+ x*x)
println(squares) // List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)
val cubes = numberFunc((xs, x) => xs :+ x*x*x)
println(cubes) // List(1, 8, 27, 64, 125, 216, 343, 512, 729, 1000)
Сравнение с «каррированием»
Иногда можно встретить, что метод с несколькими списками параметров называется «каррированный».
Как говорится в статье на Википедии о каррировании,
Каррирование — преобразование функции от многих аргументов в набор вложенных функций, каждая из которых является функцией от одного аргумента.
Мы не рекомендуем использовать слово «каррирование» в отношении множественных списков параметров Scala по двум причинам:
-
В Scala множественные параметры и множественные списки параметров задаются и реализуются непосредственно как часть языка, а не преобразуются из функций с одним параметром.
-
Существует опасность путаницы с методами из стандартной Scala библиотеки
curried
иuncurried
, которые вообще не включают множественные списки параметров.
Тем не менее, несомненно, есть сходство между множественными списками параметров и каррированием. Хотя они различаются в месте определения, в месте вызова могут тем не менее выглядеть одинаково, как в этом примере:
// версия с множественными списками параметров
def addMultiple(n1: Int)(n2: Int) = n1 + n2
// два различных способа получить каррированную версию
def add(n1: Int, n2: Int) = n1 + n2
val addCurried1 = (add _).curried
val addCurried2 = (n1: Int) => (n2: Int) => n1 + n2
// независимо от определения, вызов всех трех идентичен
addMultiple(3)(4) // 7
addCurried1(3)(4) // 7
addCurried2(3)(4) // 7
Contributors to this page:
Contents
- Введение
- Основы
- Единобразие типов
- Классы
- Значения Параметров По умолчанию
- Именованные Аргументы
- Трейты
- Кортежи
- Композиция классов с трейтами
- Функции Высшего Порядка
- Вложенные Методы
- Множественные списки параметров (Каррирование)
- Классы Образцы
- Сопоставление с примером
- Объекты Одиночки
- Регулярные Выражения
- Объект Экстрактор
- Сложные for-выражения
- Обобщенные Классы
- Вариантность
- Верхнее Ограничение Типа
- Нижнее Ограничение Типа
- Внутренние классы
- Члены Абстрактного Типа
- Составные Типы
- Самоописываемые типы
- Контекстные параметры, также известные, как неявные параметры
- Неявные Преобразования
- Полиморфные методы
- Выведение Типа
- Операторы
- Вызов по имени
- Аннотации
- Пакеты и Импорт
- Объекты Пакета