2011-06-07 15 views
8

में उच्च समझ कार्यों के विरुद्ध सूची समझ एसएमएल पृष्ठभूमि से आती है और उच्च-आदेश कार्यों के साथ काफी सहज महसूस करती है। लेकिन मुझे वास्तव में सूची समझ का विचार नहीं मिलता है। क्या ऐसी कोई स्थिति है जहां सूची समझ List पर उच्च-आदेश कार्यों की तुलना में अधिक उपयुक्त है और इसके विपरीत?एफ #

मैंने कहीं सुना है कि सूची समझ उच्च-आदेश कार्यों की तुलना में धीमी है, क्या मुझे प्रदर्शन-महत्वपूर्ण कार्यों को लिखते समय इसका उपयोग करने से बचना चाहिए?

उदाहरण के लिए 'खातिर, Projecting a list of lists efficiently in F# पर एक नज़र डालें जहां @ cfern का जवाब दो संस्करणों क्रमशः सूची समझ और उच्च आदेश कार्यों का उपयोग कर शामिल हैं:

let rec cartesian = function 
    | [] -> [[]] 
    | L::Ls -> [for C in cartesian Ls do yield! [for x in L do yield x::C]] 

और:

let rec cartesian2 = function 
    | [] -> [[]] 
    | L::Ls -> cartesian2 Ls |> List.collect (fun C -> L |> List.map (fun x->x::C)) 

उत्तर

11

चुनना समझ और उच्च-आदेश कार्यों के बीच ज्यादातर शैली का मामला है। मुझे लगता है कि समझ कभी-कभी अधिक पठनीय होती है, लेकिन यह सिर्फ एक व्यक्तिगत वरीयता है। ध्यान दें कि cartesian समारोह इस तरह अधिक सुंदर ढंग से लिखा जा सकता है:

let rec cartesian = function 
    | [] -> [[]] 
    | L::Ls -> 
    [ for C in cartesian Ls do for x in L do yield x::C ] 

दिलचस्प मामला है, जब पुनरावर्ती कार्यों लेखन। आप दृश्यों (और अनुक्रम comprehensions) का उपयोग करते हैं, वे अस्थायी सूची के कुछ अनावश्यक आवंटन को हटा दें और यदि आप एक पूंछ-कॉल स्थिति में yield! उपयोग करते हैं, आप भी ढेर अतिप्रवाह अपवाद से बचने कर सकते हैं:

let rec nums n = 
    if n = 100000 then [] 
    else n::(nums (n+1)) 
// throws StackOverflowException 
nums 0 

let rec nums n = seq { 
    if n < 100000 then 
    yield n 
    yield! nums (n+1) } 
// works just fine 
nums 0 |> List.ofSeq 

यह काफी एक दिलचस्प है पैटर्न, क्योंकि इसे सूचियों का उपयोग करके उसी तरह लिखा नहीं जा सकता है। सूचियों का उपयोग करते समय, आप कुछ तत्व वापस नहीं कर सकते हैं और फिर एक रिकर्सिव कॉल कर सकते हैं, क्योंकि यह n::(nums ...) से मेल खाता है, जो पूंछ-पुनरावर्ती नहीं है।

4

आईएलएसपी में जेनरेट कोड को देखते हुए, आप देख सकते हैं कि सूची मशीनों को राज्य मशीनों (जैसे yield return का उपयोग करके सी #) में संकलित किया गया है, फिर List.ofSeq जैसे कुछ को पास किया गया। दूसरी तरफ उच्च-आदेश कार्य हाथ से कोडित होते हैं, और अक्सर म्यूटेबल राज्य या अन्य अनिवार्य संरचनाओं को यथासंभव कुशल होने के लिए उपयोग करते हैं। जैसा कि अक्सर होता है, सामान्य उद्देश्य तंत्र अधिक महंगा होता है।

तो, अपने प्रश्न का उत्तर देने के लिए, यदि प्रदर्शन महत्वपूर्ण है तो आम तौर पर आपकी समस्या के लिए विशिष्ट उच्च-आदेश फ़ंक्शन होता है जिसे प्राथमिकता दी जानी चाहिए।

0

टॉमस पेट्रीसेक के उत्तर में जोड़ना। आप सूची संस्करण पूंछ रिकर्सिव बना सकते हैं।

let nums3 n = 
    let rec nums3internal acc n = 
     if n = 100000 then 
      acc 
     else 
      nums3internal (n::acc) (n+1) //Tail Call Optimization possible 

    nums3internal [] n |> List.rev 

nums3 0 

काफी गतिशील के अतिरिक्त लाभ के साथ। कम से कम जब मैं स्टॉपवॉच टूल के साथ मापा जाता है तो मुझे मिलता है। (संख्या 2 सीक का उपयोग कर एल्गोरिदम है)।

Nums2 takes 81.225500ms 
Nums3 takes 4.948700ms 

उच्च संख्या के लिए यह लाभ कम हो जाता है, क्योंकि List.rev अक्षम है। जैसे 10000000 के लिए मुझे मिलता है:

Nums2 takes 11054.023900ms 
Nums3 takes 8256.693100ms