English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Основной курс Kotlin

Kotlin управление потоком

Функции Kotlin

Строки Kotlin

Kotlin面向对象(OOP)

Рекурсия и конечная рекурсия в Kotlin

В этой статье вы узнаете, как создавать рекурсивные функции. Функция, которая вызывает себя. Кроме того, вы также узнаете о функциях с尾 recursion.

которая вызывает себяфункцияНазваны рекурсивные функции. Кроме того, эта техника называется рекурсией.

Одним из примеров в физическом мире является放置两个平行的镜子相对。 Любой объект между ними будет рекурсивно отражен.

Как работает рекурсия в программировании?

fun main(args: Array<String>) {
    ... .. ...
    recurse()
    ... .. ...
}
fun recurse() {
    ... .. ...
    recurse()
    ... .. ...
}

Здесь вызывается функция recurse() из тела самой функции recurse(). Принцип работы программы таков:

Здесь рекурсивный вызов продолжается вечно,从而导致 бесконечную рекурсию.

Чтобы избежать бесконечной рекурсии, можно использовать рекурсивный вызов в одной ветке и не рекурсивный вызов в другой ветке.if ... else(или аналогичный метод).

Пример: использование рекурсии для поиска факториала числа

fun main(args: Array<String>) {
    val number = 4
    val result: Long
    result = factorial(number)
    println("$number! = $result")
}
fun factorial(n: Int): Long {}}
    return if (n == 1) n.toLong() else n*факториал(n-1)
}

При выполнении этой программы, вывод будет:

4! = 24

Как работает этот программа?

Функция factorial() на рисунке показывает рекурсивные вызовы этой функции:

Приведенные ниже шаги включают:

факториал(4)              // Первый вызов функции, параметр: 4
4*факториал(3)            // Второй вызов функции, параметр: 3
4*(3*факториал(2))        // Третий вызов функции, параметр: 2
4*(3*(2*факториал(1)))    // Четвертый вызов функции, параметр: 1 
4*(3*(2*1))                 
24

Хвостовая рекурсия в Kotlin

Хвостовая рекурсия не является особенностью языка программирования Kotlin, а является общим понятием. Некоторые языки программирования, включая Kotlin, используют его для оптимизации рекурсивных вызовов, в то время как другие языки (например, Python) их не поддерживают.

Что такое хвостовая рекурсия?

В обычной рекурсии сначала выполняются все рекурсивные вызовы, а затем на основе возвращаемых значений вычисляется результат (как показано в примере выше). Поэтому, вы не получите результат до выполнения всех рекурсивных вызовов.

В хвостовой рекурсии сначала выполняются вычисления, затем вызывается рекурсивная функция (рекурсивный вызов передает результат текущего шага следующему рекурсивному вызову). Это делает рекурсивный вызов эквивалентным циклу и предотвращает риск переполнения стека.

Условия хвостовой рекурсии

Если вызов herself функции является последней операцией, которую она выполняет, то рекурсивная функция может выполнять хвостовую рекурсию. Например:

Пример 1:Не соответствует условиям хвостовой рекурсии, так как функция, вызываемая herself, n*факториал(n-1), не является последней операцией.

fun factorial(n: Int): Long {}}
    if (n == 1) {
        return n.toLong()
    } else {
        return n*factorial(n - 1)     
    }
}

Пример 2:Соответствует условиям хвостовой рекурсии, так как вызов функции herself fibonacci(n-1, a+b, a) является последней операцией.

fun fibonacci(n: Int, a: Long, b: Long): Long {
    return if (n == 0) b else fibonacci(n-1, a+b, a)
}

Чтобы сообщить компилятору о том, что в Kotlin выполняется хвостовая рекурсия, вам нужно пометить функцию атрибутом tailrec.

Пример: хвостовая рекурсия

import java.math.BigInteger
fun main(args: Array<String>) {
    val n = 100
    val first = BigInteger("0")
    val second = BigInteger("1")
    println(fibonacci(n, first, second))
}
tailrec fun fibonacci(n: Int, a: BigInteger, b: BigInteger): BigInteger {
    return if (n == 0) a else fibonacci(n-1, b, a+b)
}

При выполнении этой программы, вывод будет:

354224848179261915075

Эта программа вычисляет 100-е значение последовательности Фибоначчи. Поскольку результат может быть очень большим целым числом, мы импортируем класс BigInteger из стандартной библиотеки Java.

Здесь функция fibonacci() помечена атрибутом trarec, что делает ее квалифицированной для хвостовой рекурсии. Поэтому в этом случае компилятор оптимизирует рекурсию.

Если вы пытаетесь найти 20000-е (или любое другое большое целое число) значение последовательности Фибоначчи без использования хвостовой рекурсии, компилятор выбросит исключение java.lang.StackOverflowError.
Но наш предыдущий программный код работает хорошо. Это связано с тем, что мы используем хвостовую рекурсию, которая использует эффективную версию на основе цикла, а не традиционную рекурсию.

Пример: использование хвостовой рекурсии для факториала

В указанном примере (первом примере) пример расчета факториала числа не может быть оптимизирован для хвостовой рекурсии. Это другой程序, выполняющий ту же задачу.

fun main(args: Array<String>) {
    val number = 5
    println("$number! = ${factorial(number)}")
}
tailrec fun factorial(n: Int, run: Int = 1): Long {
    return if (n == 1) run.toLong() else factorial(n-1, run*n)
}

При выполнении этой программы, вывод будет:

5! = 120

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