Scala 3 — Book

Собственный map

Language

Теперь, когда известно, как писать собственные функции высшего порядка, рассмотрим более реальный пример.

Представим, что у класса List нет метода map, и есть необходимость его написать. Первым шагом при создании функций является точное определение проблемы. Сосредоточившись только на List[Int], получаем:

Необходимо написать метод map, который можно использовать для применения функции к каждому элементу в List[Int], возвращая преобразованные элементы в виде нового списка.

Учитывая это утверждение, начнем писать сигнатуру метода. Во-первых, известно, что функция должна приниматься в качестве параметра, и эта функция должна преобразовать Int в какой-то общий тип A, поэтому получаем:

def map(f: (Int) => A)

Синтаксис использования универсального типа требует объявления этого символа типа перед списком параметров, поэтому добавляем объявление типа:

def map[A](f: (Int) => A)

Далее известно, что map также должен принимать List[Int]:

def map[A](f: (Int) => A, xs: List[Int])

Наконец, также известно, что map возвращает преобразованный список, содержащий элементы универсального типа A:

def map[A](f: (Int) => A, xs: List[Int]): List[A] = ???

Теперь все, что нужно сделать, это написать тело метода. Метод map применяет заданную им функцию к каждому элементу в заданном списке для создания нового преобразованного списка. Один из способов сделать это - использовать выражение for:

for (x <- xs) yield f(x)
for x <- xs yield f(x)

for выражения зачастую делают код удивительно простым, и в данном случае - это все тело метода.

Объединив for с сигнатурой метода, получим автономный метод map, который работает с List[Int]:

def map[A](f: (Int) => A, xs: List[Int]): List[A] =
  for (x <- xs) yield f(x)
def map[A](f: (Int) => A, xs: List[Int]): List[A] =
  for x <- xs yield f(x)

Обобщим метод map

Обратим внимание, что выражение for не делает ничего, что зависит от типа Int внутри списка. Следовательно, можно заменить Int в сигнатуре типа параметром универсального типа B:

def map[A, B](f: (B) => A, xs: List[B]): List[A] =
  for (x <- xs) yield f(x)
def map[A, B](f: (B) => A, xs: List[B]): List[A] =
  for x <- xs yield f(x)

Получился метод map, который работает с любым списком.

Демонстрация работы получившегося map:

def double(i : Int): Int = i * 2
map(double, List(1, 2, 3))            // List(2, 4, 6)

def strlen(s: String): Int = s.length
map(strlen, List("a", "bb", "ccc"))   // List(1, 2, 3)

Теперь, когда рассмотрены методы, принимающие функции в качестве входных параметров, перейдем к методам, возвращающим функции.

Contributors to this page: