2010-06-06 4 views
5

इससे पहले मैंने अपने पहले एफ # प्रोजेक्ट पर कुछ फीडबैक का अनुरोध किया था। सवाल बंद करने से पहले क्योंकि दायरा बहुत बड़ा था, कोई इसे देखने के लिए बहुत दयालु था और कुछ प्रतिक्रिया छोड़ देता था।एफ #: शीर्ष-स्तरीय कार्यों को सदस्य विधियों में परिवर्तित करने के लाभ?

उन चीजों में से एक जो उन्होंने उल्लेख किया था, यह इंगित कर रहा था कि मेरे पास कई नियमित कार्य थे जिन्हें मेरे डेटाटाइप पर विधियों में परिवर्तित किया जा सकता था। कर्तव्यनिष्ठा मैं

let getDecisions hand = 
    let (/=/) card1 card2 = matchValue card1 = matchValue card2 
    let canSplit() = 
     let isPair() = 
      match hand.Cards with 
      | card1 :: card2 :: [] when card1 /=/ card2 -> true 
      | _ -> false 
     not (hasState Splitting hand) && isPair() 
    let decisions = [Hit; Stand] 
    let split = if canSplit() then [Split] else [] 
    let doubleDown = if hasState Initial hand then [DoubleDown] else [] 
    decisions @ split @ doubleDown 

की तरह बदल रहा है बातें माध्यम से चला गया यह करने के लिए:

type Hand 
// ...stuff... 
member hand.GetDecisions = 
    let (/=/) (c1 : Card) (c2 : Card) = c1.MatchValue = c2.MatchValue 
    let canSplit() = 
     let isPair() = 
      match hand.Cards with 
      | card1 :: card2 :: [] when card1 /=/ card2 -> true 
      | _ -> false 
     not (hand.HasState Splitting) && isPair() 
    let decisions = [Hit; Stand] 
    let split = if canSplit() then [Split] else [] 
    let doubleDown = if hand.HasState Initial then [DoubleDown] else [] 
    decisions @ split @ doubleDown 

अब, मैं शक नहीं है मैं एक बेवकूफ हूँ, लेकिन से (मेरा अनुमान है कि) बनाने सी # इंटरॉप आसान अन्य , उसने मुझे क्या हासिल किया? विशेष रूप से, मुझे कुछ डी फायदे मिले, रूपांतरण के अतिरिक्त काम की गणना नहीं की गई (जिसे मैं नहीं मानूंगा, क्योंकि मैं इसे पहली जगह में कर सकता था, मुझे लगता है, हालांकि यह एफ # इंटरएक्टिव का उपयोग करता दर्द का अधिक)। एक बात के लिए, अब मैं आसानी से "पाइपलाइनिंग" समारोह के साथ काम करने में सक्षम नहीं हूं। मुझे some |> chained |> calls से (some |> chained).Calls आदि में जाना और बदलना पड़ा। इसके अलावा, यह मेरे प्रकार के सिस्टम डंबर को बनाने लग रहा था - जबकि मेरे मूल संस्करण के साथ, मेरे कार्यक्रम को बड़े पैमाने पर सदस्य विधियों में परिवर्तित करने के बाद, कोई प्रकार की टिप्पणियों की आवश्यकता नहीं थी, मुझे त्रुटियों का एक गुच्छा मिला लुकअप उस बिंदु पर अनिश्चित होने के कारण, और मुझे जाना था और टाइप एनोटेशन जोड़ना था (इसका एक उदाहरण ऊपर (/=/) में है)।

मुझे उम्मीद है कि मैं बहुत संदिग्ध नहीं आ गया हूं, क्योंकि मुझे प्राप्त सलाह की सराहना करते हैं, और मेरे लिए मूर्खतापूर्ण कोड लिखना महत्वपूर्ण है। मैं सिर्फ उत्सुक हूं कि मुहावरे किस तरह से है :)

धन्यवाद!

उत्तर

4

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

जैसा कि आपने देखा है, नुकसान भी हैं; टाइप अनुमान विशेष रूप से उल्लेखनीय है (आपको x.Something पर कॉल करने के लिए प्राथमिकता के प्रकार को जानने की आवश्यकता है); प्रकारों और कार्यक्षमता के मामले में जो आमतौर पर उपयोग किया जाता है, दोनों के लाभ प्राप्त करने के लिए दोनों सदस्यों और कार्यों का एक मॉड्यूल प्रदान करना उपयोगी हो सकता है (यह उदाहरण है कि FSharp.Core में सामान्य डेटा प्रकारों के लिए क्या होता है)।

ये "स्क्रिप्टिंग सुविधा" बनाम "सॉफ्टवेयर इंजीनियरिंग स्केल" के विशिष्ट व्यापार-बंद हैं। व्यक्तिगत रूप से मैं हमेशा उत्तरार्द्ध की तरफ झुकता हूं।

3

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

मैं जब लेखन वैश्विक कार्यों का उपयोग कर पसंद करेंगे:

  • कार्यात्मक डेटा प्रकार जैसे पेड़/सूचियों और अन्य आम तौर पर उपयोगी डेटा संरचनाओं कि अपने कार्यक्रम के डेटा के कुछ बड़ी राशि रखने के रूप में। यदि आपका प्रकार कुछ ऑपरेशन प्रदान करता है जो उच्च ऑर्डर फ़ंक्शन के रूप में व्यक्त किए जाते हैं (उदा। List.map) तो शायद यह इस प्रकार का डेटा प्रकार है।

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

दूसरी ओर, मैं सदस्यों जब लेखन का प्रयोग करेंगे:

  • सरल प्रकार ऐसे Card गुण (मूल्य और रंग की तरह) और हो सकता है के रूप में अपेक्षाकृत सरल (या कोई) कुछ computations प्रदर्शन विधियों।

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

मैं भी है कि यह अपने आवेदन में कुछ सरल प्रकार के लिए सदस्यों को उपयोग करने के लिए (जैसे Card) और शीर्ष स्तर के कार्यों का उपयोग कर आवेदन के बाकी को लागू बिल्कुल ठीक है का उल्लेख होगा। वास्तव में, मुझे लगता है कि यह शायद आपकी स्थिति में सबसे अच्छा तरीका होगा।

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

अतिरिक्त नोट के रूप में, कॉल की चेन दोनों सदस्यों और कार्यों का उपयोग कर प्राप्त किया जा सकता:

// Using LINQ-like extension methods 
list.Where(fun a -> a%3 = 0).Select(fun a -> a * a) 
// Using F# list-processing functions 
list |> List.filter (fun a -> a%3 = 0) |> List.map (fun a -> a * a) 
+1

परेशान करते हुए, आप डॉट नोटेशन का उपयोग करके प्रत्येक विधि कॉल को एक नई पंक्ति पर नहीं डाल सकते हैं। –

7

एक अभ्यास मैं अपने एफ # प्रोग्रामिंग में इस्तेमाल दोनों ही स्थानों पर कोड का उपयोग है।

module Hand = 
    let getDecisions hand = 
    let (/=/) card1 card2 = matchValue card1 = matchValue card2 
    ... 

और सदस्य समारोह/संपत्ति में एक 'क्लिक' दे::

type Hand 
    member this.GetDecisions = Hand.getDecisions this 

इस अभ्यास भी आम तौर पर प्रयोग किया जाता है

वास्तविक क्रियान्वयन एक मॉड्यूल में रखा जाना स्वाभाविक है अन्य एफ # पुस्तकालय, उदाहरण के लिए पावरपैक में मैट्रिक्स और वेक्टर कार्यान्वयन matrix.fs।

प्रैक्टिस में, दोनों कार्यों को दोनों स्थानों पर नहीं रखा जाना चाहिए। अंतिम निर्णय डोमेन ज्ञान पर आधारित होना चाहिए।

शीर्ष-स्तर के संबंध में, अभ्यास मॉड्यूल में कार्य करना है, और यदि आवश्यक हो तो उनमें से कुछ को शीर्ष-स्तर में बनाना है। जैसे एफ # पावरपैक में, Matrix<'T> नामस्थान Microsoft.FSharp.Math में है।

[<AutoOpen>] 
module MatrixTopLevelOperators = 
    let matrix ll = Microsoft.FSharp.Math.Matrix.ofSeq ll 
1

मैं आमतौर पर कक्षाओं से बचने के तरीके के कारण कक्षाओं से बचता हूं। यदि आप ऐसा करते हैं, तो मॉड्यूल नाम को टाइप नाम के समान बनाने में कभी-कभी आसान होता है - नीचे संकलक निर्देश आसान होता है।

type Hand 
// ...stuff... 

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] 
module Hand = 
    let GetDecisions hand = //... 
+0

क्या आप उस विशेषता पर थोड़ा सा विस्तार कर सकते हैं? –

+0

मॉड्यूल .NET कक्षाओं में संकलित किए जाते हैं, इसलिए आम तौर पर आपके पास समान नामस्थान में 'टाइप हैंड' और 'मॉड्यूल हैंड' नहीं हो सकता है। उपर्युक्त विशेषता मॉड्यूल की कक्षा को इसके बजाय 'हैंड मॉड्यूल' नामित करने का कारण बनती है (जहां तक ​​.NET रनटाइम संबंधित है), जबकि अभी भी एफ # कोड को वाक्यविन्यास 'Hand.GetDecisions' का उपयोग करने की अनुमति है। –

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