2013-06-26 4 views
12

मैंने पूंछ रिकर्सिव विधि रखने के लिए @tailrec एनोटेशन का उपयोग और पढ़ा है। मैं कई लिंक से गुजर चुका हूं जो इसे समझाते हैं। उदाहरण के लिए यह केवल तभी काम करता है जब स्व-कॉलिंग फ़ंक्शंस और कंधे ओवरड्रिड आदि नहीं हो।@tailrec कैसे काम करता है

हर जगह यह उल्लेख किया गया है कि compiler optimizes। लेकिन कंपाइलर क्या पूंछ रिकर्सिव बनाने के लिए क्या जादू/अवधारणा करता है। नीचे एक सरल समारोह के लिए, संकलक क्या करना है:

@tailrec def fact(acc: Int, n: Int): Int = { 
    if (n <= 1) acc 
    else fact(n * acc, n - 1) 
} 
fact(1,10) 

मेरा मतलब है कि यह एक पाश जहां यह बार-बार यह कहता है और उसके बाद में परिवर्तित करता है अंतिम मान देता है? वहाँ जो यह

+1

मूल रूप से हाँ, स्केला संकलक कोड थोड़ी देर के पाश के समान बाईटकोड करने के लिए कोड में कनवर्ट करता है: तो यह वास्तव में कैसे यह पूंछ कॉल का अनुकूलन कर दिखाता है। यह शायद 'var acc = 1 की तरह कुछ कर रहा है; var n = 10; शुरू करें: अगर (एन <= 1) एक और वापस लौटें {acc = n * acc; एन = एन -1; गोटो शुरू} '। शरीर की शुरुआत में गेटो के साथ सभी पूंछ कॉलों को यांत्रिक रूप से प्रतिस्थापित करना संभव होना चाहिए। – huynhjl

उत्तर

11

वर्णन किया गया है इसके अलावा (कोड repasting यहाँ):

var acc = 1 
    var n = 10 
start: 
    if (n <= 1) return acc 
    else { 
    acc = n * acc 
    n = n - 1 
    goto start 
    } 

मैं हाल ही में निर्माण मैं बस के लिए हुआ के साथ और scalac -Xprint:all साथ fact विधि संकलन की कोशिश की और किसी भी तरह संकलक ने icode फ़ाइल उत्सर्जित की।

// methods 
    def fact(acc: Int (INT), n: Int (INT)): Int { 
    locals: value acc, value n, value _$this 
    startBlock: 1 
    blocks: [1,2,3,4,5] 

    1: 
    2 JUMP 2 

    2: // huynhjl's comment: IF condition is here 
    3 LOAD_LOCAL(value n) 
    3 CONSTANT(1) 
    3 CJUMP (INT)LE ? 3 : 4 

    3: // huynhjl's comment: first branch of IF, will return acc 
    3 LOAD_LOCAL(value acc) 
    3 JUMP 5 

    5: 
    2 RETURN(INT) 

    4: // huynhjl's comment: else branch of IF, update acc and n and jump back 
    4 LOAD_LOCAL(value n) 
    4 LOAD_LOCAL(value acc) 
    4 CALL_PRIMITIVE(Arithmetic(MUL,INT)) 
    4 LOAD_LOCAL(value n) 
    4 CONSTANT(1) 
    4 CALL_PRIMITIVE(Arithmetic(SUB,INT)) 
    4 STORE_LOCAL(value n) 
    4 STORE_LOCAL(value acc) 
    4 JUMP 2 

    } 
8

बताते हैं यहाँ एक अच्छा Blog post विषय

@tailrec पर केवल गारंटी देता है कि एक त्रुटि जारी किया जाता है, तो पूंछ कॉल अनुकूलन संकलक द्वारा नहीं किया जा सकता है कागज के लिए किसी भी कड़ी है। स्कैला डिफ़ॉल्ट रूप से पूंछ कॉल अनुकूलन करता है।

जब पेपर द्वारा वर्णित स्थितियों को पूरा किया जाता है, तो फ्रेम के ढेर के बजाय अंतिम फ्रेम रखना और लूप करने के लिए संभव है। प्रक्रिया बेहतर आपके प्रश्न पर मेरी टिप्पणी के लिए here

+2

मैंने इसे देखा है। यह इसकी व्याख्या नहीं करता है। उन्होंने ट्रैम्पोलिन के बारे में उल्लेख किया है जिसका प्रयोग गैर-आत्म बचाव करने वाले मेथोइड के लिए किया जा सकता है। लेकिन उन्होंने जो समाधान दिया है वह पूंछ-रिकर्सिव नहीं है – Jatin

+1

सही नहीं है: @tailrec एक संकलन त्रुटि जारी करता है, चेतावनी नहीं –

+0

@VincenzoMaggio ठीक है - मेरा मतलब है "यह आपको चेतावनी देता है" में चेतावनी का मतलब है। मैं इसे संपादित करूंगा। –

संबंधित मुद्दे