Написание своего собственного метода map
Теперь, когда известно, как писать собственные функции высшего порядка, рассмотрим более реальный пример.
Представим, что у класса 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
выражения зачастую делают код удивительно простым, и в данном случае - это все тело метода.
Объединив for
с сигнатурой метода, получим автономный метод map
, который работает с List[Int]
:
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)
Получился метод map
, который работает с любым списком.
Демонстрация работы получившегося map
:
def double(i : Int) = i * 2
def strlen(s: String) = s.length
map(double, List(1, 2, 3))
// res0: List[Int] = List(2, 4, 6)
map(strlen, List("a", "bb", "ccc"))
// res1: List[Int] = List(1, 2, 3)
Теперь, когда рассмотрены методы, принимающие функции в качестве входных параметров, перейдем к методам, возвращающим функции.
Ссылки: