निम्नलिखित में, लाइन maybeNext.map{rec}.getOrElse(n)
रिकर्स या एस्केप पैटर्न को लागू करने के लिए Option
मोनैड का उपयोग करती है।
scala> @tailrec
| def rec(n: Int): Int = {
| val maybeNext = if (n >= 99) None else Some(n+1)
| maybeNext.map{rec}.getOrElse(n)
| }
हालांकि अच्छा लग रहा है:
<console>:7: error: could not optimize @tailrec annotated method:
it contains a recursive call not in tail position
def rec(n: Int): Int = {
^
मुझे लगता है कि संकलक इस मामले में पूंछ रिकर्सन को हल करने में सक्षम होना चाहिए। यह निम्नलिखित (कुछ हद तक प्रतिकारक, लेकिन संकलन योग्य) नमूने के बराबर है:
scala> @tailrec
| def rec(n: Int): Int = {
| val maybeNext = if (n >= 99) None else Some(n+1)
| if (maybeNext.isEmpty) n
| else rec(maybeNext.get)
| }
rec: (n: Int)Int
क्या कोई यहां रोशनी दे सकता है? संकलक इसका पता क्यों नहीं लगा सकता? क्या यह एक बग है, या एक निरीक्षण है? क्या समस्या बहुत कठिन है?
संपादित करें: पहले उदाहरण से @tailrec
हटाएं और विधि संकलित करें; लूप समाप्त हो जाता है। अंतिम कॉल हमेशा getOrElse
होती है जो if option.isEmpty defaultValue else recurse
के बराबर होती है। मुझे लगता है कि यह संकलक द्वारा अनुमान लगाया जा सकता है और होना चाहिए।
1 उत्तर
यह एक बग नहीं है, यह एक निरीक्षण नहीं है, और यह एक पूंछ रिकर्सन नहीं है।
हां, आप कोड को टेल रिकर्सिव तरीके से लिख सकते हैं, लेकिन इसका मतलब यह नहीं है कि प्रत्येक समकक्ष एल्गोरिदम को टेल रिकर्सिव बनाया जा सकता है। आइए इस कोड को लें:
maybeNext.map{rec].getOrElse(n)
सबसे पहले, आखिरी कॉल getOrElse(n)
पर है। यह कॉल वैकल्पिक नहीं है - यह हमेशा की जाती है, और परिणाम को समायोजित करना आवश्यक है। लेकिन चलिए इसे अनदेखा करते हैं।
अगली से अंतिम कॉल map{rec}
पर है। नहीं से rec
तक। वास्तव में, rec
को आपके कोड में बिल्कुल नहीं कहा जाता है! कुछ अन्य फ़ंक्शन इसे कॉल करते हैं (और, वास्तव में, यह map
पर अंतिम कॉल भी नहीं है), लेकिन आपका फ़ंक्शन नहीं।
पूंछ पुनरावर्ती होने के लिए कुछ के लिए, आपको कॉल को "गोटो" के साथ बदलने में सक्षम होना चाहिए, इसलिए बोलने के लिए। ऐशे ही:
def rec(n: Int): Int = {
BEGINNING:
val maybeNext = if (n >= 99) None else Some(n+1)
if (maybeNext.isEmpty) n
else {
n = maybeNext.get
goto BEGINNING
}
}
यह दूसरे कोड में कैसे होगा?
def rec(n: Int): Int = {
BEGINNING:
val maybeNext = if (n >= 99) None else Some(n+1)
maybeNext.map{x => n = x; goto BEGINNING}.getOrElse(n)
}
यहां गोटो rec
के अंदर नहीं है। यह एक अनाम Function1
के apply
के अंदर है, जो अपनी बारी से, Option
के map
के अंदर है, इसलिए यहां एक शाखा दो स्टैक फ्रेम को छोड़ देगी प्रत्येक कॉल। मान लें कि अंतर-विधि शाखाकरण पहली जगह में संभव था।
getOrElse
है, जो `if option.isEmpty defaultValue के बराबर है, अन्यथा गोटो BEGINNING। मुझे लगता है कि यह अनुमान लगाया जा सकता है और होना चाहिए।map{recursiveCall}.getOrElse(nonRecursiveValue)
स्पष्ट रूप से तार्किक रूप से पूंछ-पुनरावर्ती है। और, इस पैटर्न की अनुमति नहीं देना (भले ही इसे संकलक तर्क में स्पष्ट रूप से सक्षम किया जाना है), इसका मतलब है कि लोग एनोटेशन छोड़ देंगे या कम सुरुचिपूर्ण कोड लिखेंगे।