ham*_*d_c 13 java recursion kotlin
我在 java 和 kotlin 中有两个几乎相同的代码
爪哇:
public void reverseString(char[] s) {
helper(s, 0, s.length - 1);
}
public void helper(char[] s, int left, int right) {
if (left >= right) return;
char tmp = s[left];
s[left++] = s[right];
s[right--] = tmp;
helper(s, left, right);
}
Run Code Online (Sandbox Code Playgroud)
科特林:
fun reverseString(s: CharArray): Unit {
helper(0, s.lastIndex, s)
}
fun helper(i: Int, j: Int, s: CharArray) {
if (i >= j) {
return
}
val t = s[j]
s[j] = s[i]
s[i] = t
helper(i + 1, j - 1, s)
}
Run Code Online (Sandbox Code Playgroud)
java 代码通过了大量输入的测试,但 kotlin 代码导致 aStackOverFlowError除非我在 kotlin 中tailrec的helper函数之前添加关键字。
我想知道为什么这个函数在 java 和 kolin 中工作,tailrec但在 kotlin 中没有tailrec?
PS:
我知道该怎么tailrec做
我想知道为什么这个函数在java和kotlin中都有效,
tailrec但在kotlin 中没有tailrec?
简短的回答是因为您的Kotlin方法比JAVA方法“更重” 。在每次调用时,它都会调用另一个“激发”的方法StackOverflowError。因此,请参阅下面更详细的说明。
Java字节码等价物 reverseString()
我相应地在Kotlin和JAVA 中检查了您的方法的字节码:
JAVA 中的 Kotlin 方法字节码
...
public final void reverseString(@NotNull char[] s) {
Intrinsics.checkParameterIsNotNull(s, "s");
this.helper(0, ArraysKt.getLastIndex(s), s);
}
public final void helper(int i, int j, @NotNull char[] s) {
Intrinsics.checkParameterIsNotNull(s, "s");
if (i < j) {
char t = s[j];
s[j] = s[i];
s[i] = t;
this.helper(i + 1, j - 1, s);
}
}
...
Run Code Online (Sandbox Code Playgroud)
JAVA中的JAVA方法字节码
...
public void reverseString(char[] s) {
this.helper(s, 0, s.length - 1);
}
public void helper(char[] s, int left, int right) {
if (left < right) {
char temp = s[left];
s[left++] = s[right];
s[right--] = temp;
this.helper(left, right, s);
}
}
...
Run Code Online (Sandbox Code Playgroud)
因此,有两个主要区别:
Intrinsics.checkParameterIsNotNull(s, "s")helper()在Kotlin版本中为每个调用。所以,让我们测试一下Intrinsics.checkParameterIsNotNull(s, "s")单独如何影响行为。
测试两种实现
我为这两种情况创建了一个简单的测试:
@Test
public void testJavaImplementation() {
char[] chars = new char[20000];
new Example().reverseString(chars);
}
Run Code Online (Sandbox Code Playgroud)
和
@Test
fun testKotlinImplementation() {
val chars = CharArray(20000)
Example().reverseString(chars)
}
Run Code Online (Sandbox Code Playgroud)
对于JAVA,测试成功而没有问题,而对于Kotlin,由于StackOverflowError. 但是,在我添加Intrinsics.checkParameterIsNotNull(s, "s")到JAVA方法之后,它也失败了:
public void helper(char[] s, int left, int right) {
Intrinsics.checkParameterIsNotNull(s, "s"); // add the same call here
if (left >= right) return;
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
helper(s, left + 1, right - 1);
}
Run Code Online (Sandbox Code Playgroud)
结论
您的Kotlin方法具有较小的递归深度,因为它Intrinsics.checkParameterIsNotNull(s, "s")在每一步都会调用,因此比JAVA方法重。如果您不想要这种自动生成的方法,那么您可以在编译期间禁用空检查,如回答here
但是,由于您了解tailrec带来的好处(将您的递归调用转换为迭代调用),您应该使用它。