Saya punya dua kode yang hampir sama di java dan kotlin
Jawa:
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)
}
Kode java lulus tes dengan input besar tetapi kode kotlin menyebabkan StackOverFlowError
kecuali saya menambahkan tailrec
kata kunci sebelum helper
fungsi di kotlin.
Saya ingin tahu mengapa fungsi ini bekerja di java dan juga di kolin dengan tailrec
tetapi tidak di kotlin tanpa tailrec
?
PS:
Saya tahu apa yang tailrec
harus dilakukan
tailrec
, atau menghindari rekursi; ukuran tumpukan yang tersedia bervariasi antara proses, antara JVM dan pengaturan, dan tergantung pada metode dan paramsnya. Tetapi jika Anda bertanya karena penasaran murni (alasan yang sangat bagus!), Maka saya tidak yakin. Anda mungkin perlu melihat bytecode.Jawaban:
Jawaban singkatnya adalah karena metode Kotlin Anda "lebih berat" daripada yang JAVA . Pada setiap panggilan itu memanggil metode lain yang "memprovokasi"
StackOverflowError
. Jadi, lihat penjelasan lebih rinci di bawah ini.Setara bytecode Java untuk
reverseString()
Saya memeriksa kode byte untuk metode Anda di Kotlin dan JAVA sesuai:
Metode bytecode Kotlin di JAVA
Metode bytecode JAVA di JAVA
Jadi, ada 2 perbedaan utama:
Intrinsics.checkParameterIsNotNull(s, "s")
dipanggil untuk masing - masinghelper()
dalam versi Kotlin .Jadi, mari kita uji seberapa
Intrinsics.checkParameterIsNotNull(s, "s")
sendirian mempengaruhi perilaku.Uji kedua implementasi
Saya telah membuat tes sederhana untuk kedua kasus:
Dan
Untuk JAVA tes berhasil tanpa masalah sedangkan untuk Kotlin gagal total karena a
StackOverflowError
. Namun, setelah saya menambahkanIntrinsics.checkParameterIsNotNull(s, "s")
ke metode JAVA gagal juga:Kesimpulan
Metode Kotlin Anda memiliki kedalaman rekursi yang lebih kecil karena memanggil
Intrinsics.checkParameterIsNotNull(s, "s")
pada setiap langkah dan karenanya lebih berat daripada rekan JAVA- nya. Jika Anda tidak ingin metode yang dibuat secara otomatis ini, maka Anda dapat menonaktifkan pemeriksaan nol selama kompilasi seperti yang dijawab di siniNamun, karena Anda memahami manfaat apa yang
tailrec
membawa (mengubah panggilan rekursif Anda menjadi iteratif), Anda harus menggunakannya.sumber
Intrinsics.checkParameterIsNotNull(...)
. Jelas, setiap susunan kerangka seperti itu membutuhkan sejumlah memori (untukLocalVariableTable
tumpukan operan dan sebagainya) ..Kotlin hanya sedikit lebih lapar (Int objek params dan params). Selain solusi tailrec yang cocok di sini, Anda dapat menghilangkan variabel lokal
temp
dengan xor-ing:Tidak sepenuhnya yakin apakah ini berfungsi untuk menghapus variabel lokal.
Juga menghilangkan j mungkin dilakukan:
sumber