2012-06-25 12 views
10

में निम्नलिखित निरंतरता इकाई का उपयोग करना:StackOverflow निरंतरता इकाई

type ContinuationMonad() = 
    member this.Bind (m, f) = fun c -> m (fun a -> f a c) 
    member this.Return x = fun k -> k x 

let cont = ContinuationMonad() 

मैं देखना क्यों निम्नलिखित मुझे एक ढेर अतिप्रवाह देता है असफल: एक ओर जहां निम्नलिखित

let map f xs = 
    let rec map xs = 
     cont { 
      match xs with 
      | [] -> return [] 
      | x :: xs -> 
       let! xs = map xs 
       return f x :: xs 
     } 
    map xs id;; 

let q = [1..100000] |> map ((+) 1) 

नहीं करता है:

let map f xs = 
    let rec map xs = 
     cont { 
      match xs with 
      | [] -> return [] 
      | x :: xs -> 
       let! v = fun g -> g(f x) 
       let! xs = map xs 
       return v :: xs 
     } 
    map xs id;; 

let q = [1..100000] |> map ((+) 1) 
+0

ध्यान दें कि मैं वी.एस. 2012 आर सी पर हूँ अगर कोई परीक्षण कर सकते हैं यह है वीएस -2010 की वर्तमान रिलीज पर वही व्यवहार। –

+0

हां, और यह ओकैमल में भी वही व्यवहार है। नीचे मेरा जवाब देखें। – t0yv0

+0

एफडब्ल्यूआईडब्ल्यू, यह व्यवहार अभी भी वीएस2015, एफ # 4.0, अपडेट 3 के साथ देखा जा सकता है (हालांकि उत्तरों का संकेत है कि इसे कंपाइलर पर दोष नहीं दिया जा सकता है)। – Abel

उत्तर

7

अपने उदाहरण को ठीक करने के लिए, इकाई की अपनी परिभाषा के लिए इस विधि जोड़ें:

member this.Delay(mk) = fun c -> mk() c 

जाहिर है बात यह है कि overflows map की पुनरावर्ती कॉल में बड़े इनपुट सूची के विनाश है। देरी से समस्या हल हो जाती है।

ध्यान रखें कि आपके दूसरे संस्करण map को पुनरावर्ती कॉल डालता है एक और let! जो Bind और एक अतिरिक्त लैम्ब्डा को desugars के पीछे, प्रभाव में map को पुनरावर्ती कॉल में देरी।

मुझे इस निष्कर्ष तक पहुंचने से पहले कुछ झूठे निशानों का पीछा करना पड़ा। StackOverflow को द्वारा भी फेंक दिया गया था (हालांकि उच्च N पर) जब तक रिकर्सिव कॉल में देरी हो रही है, तब तक StackOverflow को फेंक दिया गया है। जबकि F# TCO कुछ quirks है, OCaml अधिक सिद्ध है, इसलिए इस ने मुझे आश्वस्त किया है कि समस्या संकलक कोड और नहीं के साथ वास्तव में है:

let cReturn x = fun k -> k x 
let cBind m f = fun c -> m (fun a -> f a c) 

let map f xs = 
    (* inner map loop overflows trying to pattern-match long lists *) 
    let rec map xs = 
    match xs with 
     | [] -> cReturn [] 
     | x :: xs -> 
     cBind (map xs) (fun xs -> cReturn (f x :: xs)) in 
    map xs (fun x -> x) 

let map_fixed f xs = 
    (* works without overflowing by delaying the recursive call *) 
    let rec map xs = 
    match xs with 
     | [] -> cReturn [] 
     | x :: xs -> 
     cBind (fun c -> map xs c) (fun xs -> cReturn (f x :: xs)) in 
    map xs (fun x -> x) 

let map_fused f xs = 
    (* manually fused version avoids the problem by tail-calling `map` *) 
    let rec map xs k = 
    match xs with 
     | [] -> k [] 
     | x :: xs -> 
     map xs (fun xs -> k (f x :: xs)) in 
    map xs (fun x -> x) 
+1

आप विलंब सदस्य को जोड़ने के बिना इस समस्या को हल कर सकते हैं - जॉन पामर की प्रतिक्रिया पर मेरी टिप्पणी देखें। –

+1

@ जैकपी।, मैं मानता हूं कि देरी सदस्य जोड़ना एकमात्र फिक्स नहीं है। हालांकि, आपको इनपुट सूची से मेल खाने वाले पैटर्न में देरी होनी चाहिए ताकि यह पूरी तरह से ढेर पर न हो।यदि आप ऐसा नहीं करते हैं, तो कोड ओवरफ़्लो (यदि 'N = 100000' पर नहीं तो उच्चतर 'एन' पर होगा। – t0yv0

4

एफ # कंपाइलर कभी-कभी बहुत चालाक नहीं होता है - पहले मामले में यह map xs की गणना करता है तो f x और फिर उनसे जुड़ता है, इसलिए map xs पूंछ की स्थिति में नहीं है। दूसरे मामले में, यह आसानी से पूंछ की स्थिति में रहने के लिए map xs को पुन: व्यवस्थित कर सकता है।

+0

मैं यह देखने में असफल रहा कि वर्कफ़्लो द्वारा उत्पन्न निरंतरता के अंदर कहीं भी (f x) 'की गणना कैसे की जा सकती है। –

+0

@ डेविड ग्रेनेयर - जॉन सही है। '(f x)' वर्कफ़्लो द्वारा उत्पन्न निरंतरता के अंदर गणना की जाती है, लेकिन इसकी * स्वयं * निरंतरता के अंदर गणना नहीं की जाती है। यही है, वर्कफ़्लो केवल एक निरंतरता बनाता है जो '(f x) :: xs' को लपेटता है - इसलिए जब उस निरंतरता को बुलाया जाता है, तो यह पूंछ-कॉल को' f' 'नहीं बना सकता है। जब उस निरंतरता को निष्पादित किया जाता है, तो उसे हर बार 'एफ' कहने पर एक स्टैक फ्रेम को धक्का देना पड़ता है, और चूंकि यह फिर से कर रहा है, आपको 'स्टैक ओवरफ्लो एक्सेप्शन' मिल रहा है। आपके दूसरे उदाहरण में, एफ # कंपाइलर सब कुछ के लिए पूंछ-कॉल उत्पन्न करने में सक्षम है। –

+0

@JackP मैं टिप्पणी और आपकी प्रतिक्रिया दोनों से असहमत हूं। 'नक्शा' तुरंत लौटता है, पूंछ-कॉलिंग 'मानचित्र' प्रासंगिक नहीं है। 'एफ' रिकर्सिव नहीं है, पूंछ-कॉलिंग 'एफ' या तो प्रासंगिक नहीं है। इसके अलावा, गलती एफ # कंपाइलर के साथ नहीं है, ओकैमल मेरे परीक्षणों के आधार पर वही करता है। – t0yv0

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