Что такое higher-order functions?
Функции высшего порядка (higher-order functions) — это функции, которые либо принимают другие функции в качестве аргументов, либо возвращают функции как результат, либо и то и другое. Это один из краеугольных камней функционального программирования и активно используется в языке Kotlin, который поддерживает функции как объекты первого класса.
Основные характеристики
-
Функции — это объекты
В Kotlin функции могут быть переданы в другую функцию, сохранены в переменной или возвращены из функции. -
Функции высшего порядка могут использоваться для управления потоком выполнения
Например, можно передавать действия, которые нужно выполнить позже, или оборачивать повторяющееся поведение.
Простой пример
fun operate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
return operation(x, y)
}
fun sum(a: Int, b: Int): Int = a + b
fun main() {
val result = operate(5, 3, ::sum)
println(result) // 8
}
Здесь:
-
operate — функция высшего порядка.
-
operation — функция, переданная как параметр.
Лямбды как аргументы
Функции высшего порядка часто принимают лямбда-выражения как аргументы.
fun repeatAction(times: Int, action: (Int) -> Unit) {
for (i in 0 until times) {
action(i)
}
}
fun main() {
repeatAction(3) { index ->
println("Iteration $index")
}
}
Возврат функции как результата
fun multiplier(factor: Int): (Int) -> Int {
return { number -> number \* factor }
}
fun main() {
val triple = multiplier(3)
println(triple(5)) // 15
}
Здесь multiplier возвращает функцию, которая умножает число на заданный множитель.
Синтаксис функции высшего порядка
fun <T, R> transform(value: T, operation: (T) -> R): R {
return operation(value)
}
-
operation — функция, которая принимает T и возвращает R.
-
Типы параметров и возвращаемого значения могут быть обобщены.
Применение в стандартной библиотеке Kotlin
Множество стандартных функций Kotlin — это функции высшего порядка:
- filter, map, reduce, fold, forEach, takeWhile, onEach, let, apply, run, also, with и т. д.
Примеры:
val list = listOf(1, 2, 3, 4, 5)
val even = list.filter { it % 2 == 0 }
val doubled = list.map { it \* 2 }
val sum = list.reduce { acc, i -> acc + i }
Все эти методы принимают функции в виде лямбд.
Примеры создания DSL
Функции высшего порядка активно применяются в построении DSL (domain-specific language) в Kotlin:
fun html(block: HtmlBuilder.() -> Unit): HtmlBuilder {
val builder = HtmlBuilder()
builder.block()
return builder
}
class HtmlBuilder {
fun body(block: () -> Unit) {
println("<body>")
block()
println("</body>")
}
}
Такой подход позволяет создавать выразительные структуры вроде:
html {
body {
println("Hello")
}
}
Тип функции как значение
В Kotlin функцию можно присвоить переменной и передать как параметр:
val add: (Int, Int) -> Int = { a, b -> a + b }
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
val result = calculate(4, 2, add) // 6
Использование с inline
Когда функции высшего порядка вызываются часто (особенно в циклах), можно использовать inline, чтобы избежать создания объектов лямбд и повысить производительность.
inline fun runWithLog(action: () -> Unit) {
println("Start")
action()
println("End")
}
Частичное применение и каррирование
Функции высшего порядка можно использовать для создания частично применённых функций:
fun makeGreeting(greeting: String): (String) -> String {
return { name -> "$greeting, $name!" }
}
val sayHello = makeGreeting("Hello")
println(sayHello("Alice")) // Hello, Alice!
Пример сложной функции высшего порядка
fun <T> executeIfNotNull(value: T?, action: (T) -> Unit) {
if (value != null) action(value)
}
fun main() {
val name: String? = "Kotlin"
executeIfNotNull(name) { println("Hello, $it!") }
}
Это шаблон безопасного вызова действия только если значение не null.
Преимущества
-
Переиспользуемость: Можно писать обобщённые, повторно используемые функции.
-
Читаемость: Код становится выразительным и кратким.
-
Гибкость: Можно динамически передавать поведение в виде функции.
Функции высшего порядка — это мощный инструмент, который позволяет писать декларативный, выразительный и модульный код в стиле функционального программирования. Они особенно ценны в Android-разработке, асинхронном программировании, реализации событий, реактивных потоков, UI DSL и многом другом.