Функции высшего порядка

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

Функция высшего порядка — функция, которая принимает другую функцию как параметр и/или возвращает функцию.

Раз функции — это значения, их можно передавать в другие функции. Так появляются функции высшего порядка: они позволяют выносить общий «каркас» алгоритма, оставляя переменную часть на откуп вызывающему коду.

Функция как параметр

Тип параметра-функции записывают как (Тип) -> Тип. Внутри такую функцию просто вызывают.

fun applyTwice(x: Int, op: (Int) -> Int): Int {
    return op(op(x))
}

fun main() {
    println(applyTwice(3) { it + 1 })   // (3+1)+1
    println(applyTwice(2) { it * it })  // (2*2)=4, затем 4*4
}

Вывод:

5
16

Зачем это нужно

Функции высшего порядка убирают дублирование. Вместо десяти почти одинаковых функций пишут одну, в которую передают различающуюся логику. На этом построены map, filter и почти все операции над коллекциями.

fun main() {
    val numbers = listOf(1, 2, 3, 4, 5)
    val evens = numbers.filter { it % 2 == 0 }
    val squares = numbers.map { it * it }
    println(evens)
    println(squares)
}

Вывод:

[2, 4]
[1, 4, 9, 16, 25]

Ссылки на функции

Передавать можно не только лямбду, но и ссылку на уже существующую функцию — через оператор ::.

fun isEven(n: Int) = n % 2 == 0

fun main() {
    val numbers = listOf(1, 2, 3, 4)
    println(numbers.filter(::isEven))
}

Вывод:

[2, 4]

Как работает под капотом

Тип функции вроде (Int) -> Int — обычный тип в системе типов Kotlin. Компилятор проверяет, что переданная лямбда соответствует ожидаемому типу. Под капотом каждая лямбда становится объектом, реализующим специальный интерфейс функции, а её вызов — это вызов метода этого объекта. Поэтому функции высшего порядка не магия, а строго типизированный механизм.

Частые ошибки

  • Неверный тип функции-параметра. (Int) -> Int и (Int) -> Boolean несовместимы.
  • Дублировать почти одинаковые функции. Часто их заменяет одна функция высшего порядка.
  • Забывать :: для ссылки. Имя функции без :: воспринимается как её вызов, а не как ссылка на неё.

Итог

  • Функция высшего порядка принимает и/или возвращает функцию.
  • Тип функции записывается как (Тип) -> Тип.
  • На этом механизме построены map, filter и другие операции над коллекциями.
  • Ссылку на готовую функцию передают через ::.
Проверьте себя
1. Что такое функция высшего порядка?
AФункция с большим количеством параметров
BФункция, которая принимает или возвращает другую функцию
CФункция, объявленная в классе
DСамая быстрая функция в программе
2. Как записывается тип функции, принимающей Int и возвращающей Int?
AInt -> Int
B(Int) -> Int
Cfun(Int): Int
DInt => Int
3. Как передать ссылку на функцию isEven в filter?
Afilter(isEven)
Bfilter(::isEven)
Cfilter(&isEven)
Dfilter(isEven())