2010-04-24 10 views
9

मैं एक समारोह है कि इस प्रकार दिखता है:एफ # क्यूरिंग दक्षता?

let isInSet setElems normalize p = 
     normalize p |> (Set.ofList setElems).Contains 

इस समारोह जल्दी से जाँच करने के लिए है कि क्या एक तत्व कुछ सेट का शब्दार्थ हिस्सा है इस्तेमाल किया जा सकता; उदाहरण के लिए, जाँच करने के लिए एक फ़ाइल पथ एक HTML फ़ाइल के अंतर्गत आता है, तो:

let getLowerExtension p = (Path.GetExtension p).ToLowerInvariant() 
let isHtmlPath = isInSet [".htm"; ".html"; ".xhtml"] getLowerExtension 

हालांकि, जब मैं इस तरह से ऊपर, प्रदर्शन के रूप में एक समारोह का उपयोग समारोह शरीर के मूल्यांकन के बाद से गरीब के रूप में "isInSet" में लिखा है लगता है सभी पैरामीटर ज्ञात होने तक देरी हो सकती है - विशेष रूप से, (Set.ofList setElems).Contains जैसे परिवर्तनीय बिट्स को isHtmlPath के प्रत्येक निष्पादन का पुनर्मूल्यांकन किया जाता है।

मैं सबसे अच्छा व्यवहार कैसे प्राप्त कर सकता हूं, जबकि अभी भी अधिक कुशल व्यवहार प्राप्त कर रहा है जिसमें सेट निर्माण का प्रावधान किया गया है।

उपरोक्त सिर्फ एक उदाहरण है; मैं एक सामान्य दृष्टिकोण की तलाश में हूं जो कार्यान्वयन के विवरण में मुझे नीचे फेंकने से बचाता है - जहां संभव हो मैं कार्यान्वयन के निष्पादन आदेश जैसे विवरणों से विचलित होने से बचना चाहता हूं क्योंकि यह आमतौर पर मेरे लिए महत्वपूर्ण नहीं है और एक प्रमुख को कमजोर करता है कार्यात्मक प्रोग्रामिंग की बिक्री बिंदु।

उत्तर

5

का उत्तर दिखाता है कि सीधे बंद करने का उपयोग करके कोड को मैन्युअल रूप से कैसे अनुकूलित किया जाए। यदि यह अक्सर एक नियमित पैटर्न है जिसे आपको अक्सर उपयोग करने की आवश्यकता होती है, तो उच्च-कार्य फ़ंक्शन को परिभाषित करना भी संभव है जो दो कार्यों से कुशल कोड बनाता है - पहला तर्क जो कुछ तर्कों के पूर्व-प्रसंस्करण करता है और दूसरा जो वास्तविक प्रसंस्करण करता है जब यह शेष तर्क प्राप्त करता है।

कोड इस तरह दिखेगा:

let preProcess finit frun preInput = 
    let preRes = finit preInput 
    fun input -> frun preRes input 

let f : string list -> ((string -> string) * string) -> bool = 
    preProcess 
    Set.ofList       // Pre-processing of the first argument 
    (fun elemsSet (normalize, p) ->  // Implements the actual work to be 
     normalize p |> elemsSet.Contains) // .. done once we get the last argument 

यह एक सवाल है कि क्या यह हालांकि अधिक सुरुचिपूर्ण है ...

एक और (पागल) विचार है कि आप इस के लिए गणना एक्सप्रेशन का उपयोग कर सकता है। गणना करने वाले निर्माता की परिभाषा जो आपको ऐसा करने की अनुमति देती है वह बहुत गैर-मानक है (यह ऐसा कुछ नहीं है जो लोग आमतौर पर उनके साथ करते हैं और यह किसी भी तरह से मोनैड या किसी अन्य सिद्धांत से संबंधित नहीं है)।

type CurryBuilder() = 
    member x.Bind((), f:'a -> 'b) = f 
    member x.Return(a) = a 
let curry = new CurryBuilder() 

curry गणना, आप let! का उपयोग निरूपित करने के लिए (पूर्ववर्ती कोड मूल्यांकन करने के बाद) है कि आप समारोह के अगले तर्क ले जाना चाहते हैं:

हालांकि, यह इस लिखने के लिए संभव हो जाना चाहिए
let f : string list -> (string -> string) -> string -> bool = curry { 
    let! elems =() 
    let elemsSet = Set.ofList elems 
    printf "elements converted" 
    let! normalize =() 
    let! p =() 
    printf "calling" 
    return normalize p |> elemsSet.Contains } 

let ff = f [ "a"; "b"; "c" ] (fun s -> s.ToLower()) 
// Prints 'elements converted' here 
ff "C" 
ff "D" 
// Prints 'calling' two times 

यहाँ गणना भाव बारे में अधिक जानकारी के साथ कुछ संसाधन हैं:

  • गणना एक्सप्रेस का उपयोग करने का हमेशा की तरह आयनों में मेरी पुस्तक का निशुल्क नमूना अध्याय में वर्णित है: Chapter 12: Sequence Expressions and Alternative Workflows (पीडीएफ)

  • उपरोक्त उदाहरण अनुवाद जो the F# specification (पीडीएफ) में वर्णित पूर्ण detailes

+0

क्या आपके पास एक लिंक है जिसे आप गणना अभिव्यक्तियों के बारे में पढ़ने की सिफारिश करेंगे? मुझे विचार मिलता है लेकिन विवरण को समझ में नहीं आता है ... –

+0

@Eamon: मैंने अपने उत्तर के अंत में दो लिंक जोड़े। –

+0

महान उदाहरण और गणना के अभिव्यक्तियों के कुछ हद तक झुकाव उपयोग के लिए धन्यवाद! अंततः मुझे उन पर अधिक बारीकी से देखने के लिए आश्वस्त किया - भले ही मुझे यकीन नहीं है कि उन्हें वास्तव में यहां जरूरी है। –

2
  1. करीना चोट नहीं पहुंचाता है। करीबी कभी-कभी बंद भी पेश करता है। वे आमतौर पर भी कुशल होते हैं। मैंने पहले पूछा था this question देखें। यदि आवश्यक हो तो आप प्रदर्शन को बढ़ावा देने के लिए इनलाइन का उपयोग कर सकते हैं।

  2. हालांकि, उदाहरण में अपने प्रदर्शन समस्या मुख्य रूप से अपने कोड की वजह से है:

    normalize p |> (Set.ofList setElems).Contains

यहाँ

तुम भी आप इसे करी Set.ofList setElems प्रदर्शन करने की जरूरत है। यह ओ (एन लॉग एन) समय खर्च करता है। आपको setElems के प्रकार को F # सेट में बदलने की जरूरत है, अभी सूची नहीं है। बीटीडब्ल्यू, छोटे सेट के लिए, सूचियों का उपयोग करके पूछताछ के लिए भी सेट से तेज है।

+0

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

+0

@Eamon मैं आपके अंक देखता हूं। मुझे नहीं लगता कि एफ # अब इस तरह के अनुकूलन करता है। तो हम कुशल इंटरफेस डिजाइन करने के लिए प्रोग्रामर जिम्मेदार हैं। –

9

जब तक एफ # शुद्ध और अशुद्ध कोड के बीच अंतर नहीं करता है, मुझे संदेह है कि हम उस तरह के अनुकूलन देखेंगे। हालांकि, आप करी को स्पष्ट कर सकते हैं।

let isInSet setElems = 
    let set = Set.ofList setElems 
    fun normalize p -> normalize p |> set.Contains 

isHtmlSet अब बंद प्राप्त करने के लिए केवल एक बार isInSet फोन करेगा, एक ही समय ofList को क्रियान्वित करने पर।

+1

मुझे लगता है कि यह वही है जो मैं करूँगा - और इसे भी छोटा लिखा जा सकता है: 'आइसेट सेट सामान्यीकरण = सामान्यीकृत करें >> (Set.ofList setElems)। स्टाइलिस्टिक रूप से, हालांकि, क्या आप सामान्य कोड में अधिक सामान्य, अधिक लचीला "एक लैम्बा अभिव्यक्ति वापस कर सकते हैं" या कम लचीला, कम सामान्य लेकिन कम फ़ंक्शन संरचना संस्करण पसंद करेंगे? –

5

@ ख का जवाब स्पॉट पर है। एफ #

// effects of g once after x passed, returning new closure waiting on y 
let f x = 
    let xStuff = g x 
    fun y -> h xStuff y 

में

// effects of g only after both x and y are passed 
let f x y = 
    let xStuff = g x 
    h xStuff y 

पुनर्लेखन नहीं कर सकते जब तक कि यह जानता है कि g कोई प्रभाव है, और .नेट फ्रेमवर्क आज में, यह आमतौर पर सभी भाव के 99% के प्रभाव के बारे कारण असंभव है। जिसका मतलब है कि प्रोग्रामर अभी भी ऊपर के रूप में मूल्यांकन आदेश को स्पष्ट रूप से कोडिंग के लिए ज़िम्मेदार है।