English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
在本文中,您将学习创建递归函数。一个自我调用的函数。此外,您还将了解尾递归函数。
调用自身的函数称为递归函数。并且,这种技术称为递归。
一个物理世界的实例是将两个平行的镜子相对放置。它们之间的任何对象都将被递归地反射。
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*factorial(n-1) }
이 프로그램을 실행할 때, 출력은 다음과 같습니다:
4 阶乘 = 24
factorial()下图说明了该函数的递归调用:
涉及的步骤如下:
factorial(4) // 제1다음 함수 호출, 매개변수: 4 4*factorial(3) // 제2다음 함수 호출, 매개변수: 3 4*(3*factorial(2)) // 제3다음 함수 호출, 매개변수: 2 4*(3*(2*factorial(1)) // 제4다음 함수 호출, 매개변수: 1 4*(3*(2*1)) 24
테일 재귀는 Kotlin 언어의 특징이 아니라 일반적인 개념입니다. Kotlin을 포함한 일부 프로그래밍 언어는 재귀 호출을 최적화하기 위해 이를 사용하며, 다른 언어(예: Python)는 이를 지원하지 않습니다.
일반 재귀에서는 먼저 모든 재귀 호출을 수행한 후, 반환 값을 기반으로 결과를 계산합니다(위의 예제와 같이). 따라서, 모든 재귀 호출을 수행하기 전에 결과를 얻을 수 없습니다.
테일 재귀에서는 먼저 계산을 수행한 후 재귀 호출(재귀 호출은 현재 단계의 결과를 다음 재귀 호출에 전달합니다)을 수행합니다. 이는 재귀 호출을 루프와 동일하게 만들고 스택 오버플로우의 위험을 피합니다.
자신의 함수 호출이 마지막 작업인 경우, 해당 재귀 함수는 테일 재귀를 할 수 있습니다. 예를 들어,
예제1:자신의 함수 호출 n이 尾递归 조건을 만족하지 않으므로*factorial(n-1)는 마지막 작업이 아닙니다.
fun factorial(n: Int): Long { if (n == 1) { return n.toLong() } else { return n*factorial(n - 1) } }
예제2: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 접 heads모자이크로 함수를 표시해야 합니다.
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
이 프로그램은斐波나치 수열의 nth항을 계산합니다100항. 출력은 매우 큰 정수일 수 있으므로, 우리는 Java 표준 라이브러리에서 BigInteger 클래스를 가져왔습니다.
여기서 함수 fibonacci()는 trarec 접 heads모자이크로 표시되었으며, 이 함수는 尾递归 호출에资格이 있습니다. 따라서 컴파일러는 이 경우 재귀를 최적화했습니다.
斐波나치 수열의 nth항을 찾으려고 시도할 때 尾递归를 사용하지 않는 경우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 접 heads가 컴파일러에게 재귀를 최적화하도록 알립니다.