English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
В этой статье вы узнаете, как создавать рекурсивные функции. Функция, которая вызывает себя. Кроме того, вы также узнаете о функциях с尾 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, используют его для оптимизации рекурсивных вызовов, в то время как другие языки (например, 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, чтобы сообщить компилятору оптимизировать рекурсию.