2013-06-05 12 views
21

यह एक बहुत ही सरल उदाहरण है, लेकिन मैं करने के लिए कुछ इसी तरह कैसे करना होगा:क्या जंग में एक रिकर्सिव बंद करना संभव है?

let fact = |x: u32| { 
    match x { 
     0 => 1, 
     _ => x * fact(x - 1), 
    } 
}; 

मुझे पता है कि इस विशिष्ट उदाहरण आसानी से यात्रा के साथ किया जा सकता है, लेकिन अगर यह एक पुनरावर्ती बनाने के लिए संभव है मैं सोच रहा हूँ अधिक जटिल चीजों के लिए जंग में कार्य (जैसे पेड़ों को ट्रावर्स करना) या यदि मुझे इसके बजाय अपने स्वयं के ढेर का उपयोग करने की आवश्यकता है।

+1

निको मत्साकिस ने एक [अद्भुत पोस्ट] लिखा है (http://smallcultfollowing.com/babysteps/blog/2013/04/30/the-case-of-the-recurring-closure/) आप कैसे संभावित रूप से (ab) अभी बंद होने में रिकर्सन का उपयोग करें और यह निश्चित रूप से क्यों हटाया जाएगा (यदि यह पहले से ही 'आने वाली' में नहीं है)। निस्संदेह आप हमेशा एक फ़ंक्शन को परिभाषित कर सकते हैं जो स्वयं को कॉल करता है, लेकिन यह बाहरी चर को कैप्चर नहीं करेगा। –

उत्तर

20

ऐसा करने के कुछ तरीके हैं।

आप एक संरचना में बंद कर सकते हैं और इस संरचना को बंद करने के लिए पास कर सकते हैं। तुम भी एक समारोह में structs इनलाइन परिभाषित कर सकते हैं:

fn main() { 
    struct Fact<'s> { f: &'s Fn(&Fact, u32) -> u32 } 
    let fact = Fact { 
     f: &|fact, x| if x == 0 {1} else {x * (fact.f)(fact, x - 1)} 
    }; 

    println!("{}", (fact.f)(&fact, 5)); 
} 

यह है कि अभी तक fact बंद अंदर से परिभाषित नहीं है एक अनंत प्रकार (एक समारोह है कि एक तर्क के रूप में ही लेता है) और समस्या होने की समस्या को हल हो जाता है स्वयं जब कोई let fact = |x| {...} लिखता है और इसलिए कोई इसका संदर्भ नहीं दे सकता है।

यह जंग 1.17 में काम करता है, लेकिन संभवतः भविष्य में इसे अवैध बना दिया जा सकता है क्योंकि यह कुछ मामलों में खतरनाक है, जैसा कि the blog post The Case of the Recurring Closure में उल्लिखित है। यद्यपि कोई उत्परिवर्तन नहीं है क्योंकि यह पूरी तरह से सुरक्षित है।

fn main() { 
    fn fact(x: u32) -> u32 { if x == 0 {1} else {x * fact(x - 1)} } 

    println!("{}", fact(5)); 
} 

यह ठीक काम करता है अगर आप कुछ भी कब्जा करने के लिए की जरूरत नहीं है:


एक अन्य विकल्प सिर्फ एक fn आइटम है, जो भी एक समारोह में इनलाइन परिभाषित किया जा सकता के रूप में एक पुनरावर्ती समारोह लिखना है पर्यावरण से


एक और विकल्प fn आइटम समाधान का उपयोग करने लेकिन स्पष्ट रूप से आर्ग/पर्यावरण आप चाहते हैं पार जाते हैं।

fn main() { 
    struct FactEnv { base_case: u32 } 
    fn fact(env: &FactEnv, x: u32) -> u32 { 
     if x == 0 {env.base_case} else {x * fact(env, x - 1)} 
    } 

    let env = FactEnv { base_case: 1 }; 
    println!("{}", fact(&env, 5)); 
} 

ये सभी कार्य जंग 1.17 के साथ काम करते हैं और शायद संस्करण 0.6 के बाद से काम करते हैं। fnfn के अंदर परिभाषित शीर्ष स्तर पर परिभाषित लोगों के लिए अलग नहीं हैं, सिवाय इसके कि वे fn के भीतर ही पहुंच योग्य हैं, इन्हें अंदर परिभाषित किया गया है।

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