kotlinではなく、JavaでStackOverflowErrorが原因再帰メソッド呼び出し

hamid_c:

私は、Javaとkotlinに2つのほとんど同じコードを持っています

Javaの:

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);
}

Kotlin:

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)
}

Javaコードは、巨大な入力が、原因kotlinコードとテストに合格しStackOverFlowError、私は追加しない限り、tailrec前にキーワードをhelperkotlinで機能。

私は、この機能は、Javaにもとコリンに働く理由を知りたいのtailrecではなくなしkotlinにtailrec

PS:私は何を知っていtailrecません

Anatolii:

私は、この関数がで働く理由を知りたいのjavaともにkotlintailrecはなく、中にkotlinなしtailrec

あなたのため、短い答えはKotlinの方法は、より「重い」ですJAVAの 1。すべての呼び出しで、それは「誘発」という別のメソッドを呼び出しますStackOverflowErrorだから、以下のより詳細な説明を参照してください。

以下のためのJavaバイトコードの同等物 reverseString()

私はあなたの中にメソッドのバイトコードをチェックしKotlinJAVAに対応:

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);
    }
}
...

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);
    }
}
...

だから、2つの主な違いがあります:

  1. Intrinsics.checkParameterIsNotNull(s, "s")それぞれに呼び出されるhelper()Kotlinのバージョン。
  2. で左と右の指標JAVAの中にいる間方法は、インクリメントますKotlin新しいインデックスが各再帰呼び出しのために作成されます。

だから、LETのテストはどのようにIntrinsics.checkParameterIsNotNull(s, "s")一人で行動に影響を与えます。

両方の実装をテスト

私は両方のケースのための簡単なテストを作成しました:

@Test
public void testJavaImplementation() {
    char[] chars = new char[20000];
    new Example().reverseString(chars);
}

そして

@Test
fun testKotlinImplementation() {
    val chars = CharArray(20000)
    Example().reverseString(chars)
}

以下のために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);
}

結論

あなたのKotlinのそれは呼び出しの方法は、より小さな再帰の深さを持っているIntrinsics.checkParameterIsNotNull(s, "s")すべてのステップで、したがって、そのより重いJAVAの対応。あなたは、この自動生成されたメソッドを使用しない場合は答えとして、あなたは、コンパイル時にヌルチェックを無効にすることができ、ここで

しかし、あなたは利益が何かを理解するのでtailrec(反復1にあなたの再帰呼び出しに変換)もたらし、あなたはそのいずれかを使用する必要があります。

おすすめ

転載: http://10.200.1.11:23101/article/api/json?id=442868&siteId=1