मुझे पता है कि क्यों इस समारोह में काम करता है चाहते हैं जावा में है और यह भी kotlin साथ tailrec
लेकिन में नहीं kotlin बिना tailrec
?
संक्षिप्त उत्तर इसलिए है क्योंकि आपकी कोटलिन विधि JAVA की तुलना में "भारी" है । हर कॉल पर यह एक और तरीका कहता है जो "उत्तेजित करता है" StackOverflowError
। तो, नीचे एक अधिक विस्तृत विवरण देखें।
के लिए जावा बाइटकोड समतुल्य reverseString()
मैंने कोटलिन और जावा में आपके तरीकों के लिए बाइट कोड की जाँच की :
JAVA में कोटलिन विधि bytecode
...
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 विधि बायोटेक
...
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 मुख्य अंतर हैं:
Intrinsics.checkParameterIsNotNull(s, "s")
कोटलिन संस्करण helper()
में प्रत्येक के लिए आमंत्रित किया गया है ।
- जेएवीए विधि में बाएं और दाएं सूचकांकों में वृद्धि होती है, जबकि कोटलिन में प्रत्येक पुनरावर्ती कॉल के लिए नए सूचकांक बनाए जाते हैं।
तो, आइए परीक्षण करें कि 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)
}
के लिए जावा परीक्षण समस्याओं के बिना सफल रहा थोड़ी देर के लिए Kotlin यह एक की वजह से बुरी तरह विफल रही है StackOverflowError
। हालांकि, के बाद मैं जोड़ा Intrinsics.checkParameterIsNotNull(s, "s")
करने के लिए जावा यह रूप में अच्छी तरह विफल रहा है विधि:
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);
}
निष्कर्ष
आपकी कोटलिन पद्धति में एक छोटी पुनरावृत्ति गहराई है क्योंकि यह Intrinsics.checkParameterIsNotNull(s, "s")
हर कदम पर आह्वान करती है और इस तरह यह अपने जावा समकक्ष की तुलना में भारी है । यदि आप इस स्वतः-जनरेटेड विधि को नहीं चाहते हैं, तो आप संकलन के दौरान अशक्त जाँच को अक्षम कर सकते हैं जैसा कि यहाँ पर उत्तर दिया गया है
हालाँकि, जब से आप समझते हैं कि क्या लाभ tailrec
लाता है (अपने पुनरावर्ती कॉल को पुनरावृत्ति में परिवर्तित करता है) आपको उस एक का उपयोग करना चाहिए।
tailrec
पुनरावृत्ति से बचने या उपयोग करने का एकमात्र अच्छा समाधान है ; उपलब्ध स्टैक आकार JVMs और सेट-अप के बीच, और विधि और उसके परिमाण के बीच, रन के बीच भिन्न होता है। लेकिन अगर आप शुद्ध जिज्ञासा (पूरी तरह से अच्छा कारण!) से बाहर पूछ रहे हैं, तो मुझे यकीन नहीं है। आप शायद bytecode को देखने की आवश्यकता होगी।