Теперь, когда известно, как писать собственные функции высшего порядка, рассмотрим более реальный пример.
Представим, что у класса 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:
Contents
- Введение
- Возможности Scala
- Почему Scala 3?
- Почувствуй Scala
- Пример 'Hello, World!'
- REPL
- Переменные и типы данных
- Структуры управления
- Моделирование данных
- Методы
- Функции первого класса
- Одноэлементные объекты
- Коллекции
- Контекстные абстракции
- Верхнеуровневые определения
- Обзор
- Первый взгляд на типы
- Интерполяция строк
- Структуры управления
- Моделирование предметной области
- Инструменты
- Моделирование ООП
- Моделирование ФП
- Методы
- Особенности методов
- Main методы в Scala 3
- Обзор
- Функции
- Анонимные функции
- Параметры функции
- Eta расширение
- Функции высшего порядка
- Собственный map
- Создание метода, возвращающего функцию
- Обзор
- Пакеты и импорт
- Коллекции в Scala
- Типы коллекций
- Методы в коллекциях
- Обзор
- Функциональное программирование
- Что такое функциональное программирование?
- Неизменяемые значения
- Чистые функции
- Функции — это значения
- Функциональная обработка ошибок
- Обзор
- Типы и система типов
- Определение типов
- Параметризованные типы
- Пересечение типов
- Объединение типов
- Алгебраические типы данных
- Вариантность
- Непрозрачные типы
- Структурные типы
- Зависимые типы функций
- Другие типы
- Контекстные абстракции
- Методы расширения
- Параметры контекста
- Контекстные границы
- Given импорты
- Классы типов
- Многостороннее равенство
- Неявное преобразование типов
- Обзор
- Параллелизм
- Scala утилиты
- Сборка и тестирование проектов Scala с помощью Sbt
- Рабочие листы
- Взаимодействие с Java