2009-02-01 21 views
43

मैं यह समझने की कोशिश कर रहा हूं कि कई प्रकार के पैरामीटर (जैसे int और int64) पर काम करने वाले फ़ंक्शन को कैसे परिभाषित किया जाए। जैसा कि मैं इसे समझता हूं, एफ # में फ़ंक्शन अधिभार संभव नहीं है (निश्चित रूप से संकलक शिकायत करता है)। उदाहरण के लिए निम्नलिखित कार्य करें।जेनेरिक पैरामीटर प्रकारों के साथ कार्य

let sqrt_int = function 
    | n:int -> int (sqrt (float n)) 
    | n:int64 -> int64 (sqrt (float n)) 

निश्चित रूप से संकलक शिकायत है कि वाक्य रचना, अवैध है (पैटर्न मिलान में प्रकार की कमी समर्थित नहीं हैं ऐसा लगता है), हालांकि मैं इस दिखाता है मैं प्राप्त करने के लिए चाहते हैं क्या सोचते हैं: एक समारोह है कि कई पैरामीटर प्रकार पर चल रही है और अनुसार प्रकार का एक मूल्य देता है। मुझे एहसास है कि सामान्य प्रकार/प्रकार अनुमान/पैटर्न मिलान के कुछ संयोजन का उपयोग करके एफ # में यह संभव है, लेकिन वाक्यविन्यास ने मुझे दूर कर दिया है। मैंने इसका उपयोग करने का भी प्रयास किया है:? ऑपरेटर (गतिशील प्रकार परीक्षण) और जब पैटर्न मिलान ब्लॉक में खंड, लेकिन यह अभी भी सभी प्रकार की त्रुटियों का उत्पादन करता है।

जैसा कि मैं भाषा के लिए नया हूं, मैं यहां कुछ असंभव करने की कोशिश कर रहा हूं, इसलिए वैकल्पिक समाधान होने पर कृपया मुझे बताएं।

उत्तर

58

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

  • , विधियों (एक प्रकार के सदस्य) पर अधिक भार है, जिसमें मामले ज्यादा अन्य नेट भाषाओं में के रूप में की तरह काम करता है अधिक भार (आप तदर्थ अधिभार सदस्यों कर सकते हैं का प्रयोग करें, प्रदान की गई कॉलों को संख्या/प्रकार के पैरामीटर द्वारा अलग किया जा सकता है)
  • फ़ंक्शंस पर विज्ञापन-प्रसार ओवरलोडिंग के लिए "इनलाइन", "^", और स्थिर सदस्य बाधाओं का उपयोग करें (यह वही है जो विभिन्न गणित ऑपरेटरों को काम करने की आवश्यकता है int/float/etc पर; यहां वाक्यविन्यास अजीब है, यह F # लाइब्रेरी के अलावा थोड़ा-सा उपयोग किया जाता है)
  • एक अतिरिक्त शब्दकोश-संचालन पैरामीटर पारित करके कक्षाओं को अनुकरण करें (यह वही है जो अनौपचारिक में से एक में करता है एफ # पावरपैक ली braries मनमाने ढंग से उपयोगकर्ता-निर्धारित प्रकार के लिए विभिन्न मठ एल्गोरिदम)
  • पतन वापस गतिशील टाइपिंग के लिए (एक 'obj' पैरामीटर में पारित, एक गतिशील प्रकार परीक्षण करना, फेंक बुरा प्रकार के लिए एक क्रम अपवाद) सामान्यीकरण करने के लिए

अपने विशेष उदाहरण के लिए, मैं शायद सिर्फ विधि से अधिक भार का प्रयोग करेंगे:

type MathOps = 
    static member sqrt_int(x:int) = x |> float |> sqrt |> int 
    static member sqrt_int(x:int64) = x |> float |> sqrt |> int64 

let x = MathOps.sqrt_int 9 
let y = MathOps.sqrt_int 100L 
+0

के लिए शुभकामनाएं गुड स्पष्टीकरण - मुझे लगता है कि मैं समझ रहा हूं कि अब क्या हो रहा है। इसके लिए चीयर्स। चीजों पर पढ़ने के बाद, ऐसा लगता है कि एफ # अभी भी कुछ अच्छी सुविधाओं को कार्यात्मक भाषाओं में लापता है जैसे कि हास्केल के पास है। शायद इन सुविधाओं को जल्द ही कार्यान्वित किया जाएगा कि एफ # एक प्रथम श्रेणी .NET भाषा है। – Noldorin

+0

यह सुनिश्चित नहीं है कि आप इसे 'जीवित' प्रश्न या नहीं चाहते हैं, लेकिन वर्तमान एफ # रिलीज के बाद अब अब इसकी आवश्यकता नहीं है OverloadId attrib (yey!) आप उत्तर को समायोजित करना चाहते हैं ... – ShuggyCoUk

+0

धन्यवाद, मैं पहले ही कोड अपडेट कर चुका हूं, लेकिन गद्य को अपडेट करना भूल गया। – Brian

14

हां, यह किया जा सकता है। this hubFS thread पर एक नज़र डालें।

इस मामले में, समाधान होगा:

let inline retype (x:'a) : 'b = (# "" x : 'b #) 
let inline sqrt_int (n:'a) = retype (sqrt (float n)) : 'a 

चेतावनी: कोई संकलन समय प्रकार की जाँच। अर्थात। sqrt_int "blabla" ठीक संकलित करता है लेकिन आपको रनटाइम पर FormatException मिलेगा।

+0

धन्यवाद, यह समाधान प्रतीत होता है (हालांकि यह अपेक्षाकृत सरल नहीं है जैसा कि मैंने आशा की थी)। बस स्पष्ट करने के लिए, मुझे ऐसा कुछ चाहिए? इनलाइन sqrt_int (n:^a) = retype (sqrt (float n)):^ – Noldorin

+1

Yup, जो काम करता है। हालांकि, ध्यान रखें कि इसके साथ आप संकलन-समय प्रकार की जांच खो देते हैं। अर्थात। sqrt_int "blabla" टाइप-चेक हालांकि आपको रनटाइम पर FormatException मिलेगा। –

+0

ठीक है, तो इस मामले में टोपी ऑपरेटर का उपयोग करने का कोई मतलब नहीं है, है ना? अगर मैं एक अंकगणितीय ऑपरेटर का उपयोग कर रहा हूं जैसे कि * फ़ंक्शन में (कास्ट से पहले), क्या वह संकलन-समय की जांच का बीमा करेगा? – Noldorin

2

नहीं सही जवाब पहले से ही प्रदान की से दूर ले करने के लिए है, लेकिन आप पैटर्न मिलान में तथ्य उपयोग प्रकार की कमी में कर सकते हैं। वाक्य रचना है:

| :? type -> 

या यदि आप प्रकार की जाँच और कास्टिंग संयोजित करना चाहते हैं:

| :? type as foo -> 
+0

यही वह है जिसे मैंने शुरू में सोचा था कि मैं ऐसा करने में सक्षम हूं। दुर्भाग्य से यह "रनटाइम जबरन" त्रुटि देता है (त्रुटि FS0008)। मशच के पोस्ट में एक लिंक में प्रदान किए गए रीटइप फ़ंक्शन के साथ, हालांकि, यदि मैं इसे ठीक से समझता हूं तो इसे इनलाइन कीवर्ड के विकल्प के रूप में काम करना चाहिए। – Noldorin

+0

रनटाइम जबरन मुक्केबाजी से बचा जा सकता है वैरिएबल प्रकार 'मिलान बॉक्स वैरिएबल' – Remko

9

यहाँ क्रम प्रकार चेकों का उपयोग कर एक और तरीका है ...

let sqrt_int<'a> (x:'a) : 'a = // ' 
    match box x with 
    | :? int as i -> downcast (i |> float |> sqrt |> int |> box) 
    | :? int64 as i -> downcast (i |> float |> sqrt |> int64 |> box) 
    | _ -> failwith "boo" 

let a = sqrt_int 9 
let b = sqrt_int 100L 
let c = sqrt_int "foo" // boom 
+0

के साथ मिलान पर मिलान किया जाता है। अब मेरे पास बहुत सारे विकल्प हैं! एक प्रश्न: जब आप एक्स पर एक प्रकार की बाधा निर्दिष्ट करते हैं तो आपको जेनेरिक विनिर्देशक <'a> क्यों चाहिए। मैंने सोचा कि वे समकक्ष वाक्यविन्यास थे। – Noldorin

+0

आप वास्तव में <'a> (इसे आज़माएं) छोड़ सकते हैं। संभावित perf अंतर ध्यान दें; विधि-अधिभार निर्धारण संकलन-समय पर कौन सा संस्करण निर्धारित करता है, जबकि यह संस्करण रन-टाइम प्रकार-जांच करता है। (हो सकता है कि 'sqrt' इन विचारों को खत्म कर देता है, हालांकि, मैंने माप नहीं लिया है।) – Brian

+0

और निश्चित रूप से दूसरा अंतर यह है कि यह संस्करण गैर-इनट्स के लिए संकलित करता है (और रनटाइम पर फेंकता है), जबकि विधि ओवरलोडिंग संस्करण होगा गैर-इनट्स के लिए संकलन-समय त्रुटि। – Brian

13

यह काम करता है:

type T = T with 
    static member ($) (T, n:int ) = int (sqrt (float n)) 
    static member ($) (T, n:int64) = int64 (sqrt (float n)) 

let inline sqrt_int (x:'t) :'t = T $ x 

यह जो तर्क के प्रकार पर एक संकलन समय देखने में आता है स्थिर की कमी और अधिक भार का उपयोग करता है,।

स्थिर बाधाओं स्वचालित रूप से एक ऑपरेटर (इस मामले में ऑपरेटर $) की उपस्थिति में उत्पन्न कर रहे हैं लेकिन यह हमेशा हाथ से लिखा जा सकता है:

type T = T with 
    static member Sqr (T, n:int ) = int (sqrt (float n)) 
    static member Sqr (T, n:int64) = int64 (sqrt (float n)) 

let inline sqrt_int (x:'N) :'N = ((^T or ^N) : (static member Sqr: ^T * ^N -> _) T, x) 

इस here के बारे में अधिक।

+1

ब्रायन और मॉरिसियो द्वारा प्रदान किए गए समाधान पर यह एक बहुत अच्छा सुधार है, इसे डॉट-नोटेशन (ब्रायन की तुलना करें) के बिना किसी फ़ंक्शन का लाभ होता है और यह संकलन-समय प्रकार की जांच (मॉरीसिओ की तुलना करें) जोड़ता है। क्या आप इस बात पर विस्तार से विस्तार करेंगे कि यह कैसे काम करता है और क्या ऑपरेटर-परिभाषा एक आवश्यकता है? – Abel

+2

धन्यवाद @ एबेल, मैंने समाधान के बारे में कुछ और जानकारी दी और अधिक जानकारी के साथ ब्लॉग एंट्री के लिए एक लिंक शामिल किया। मॉरीसिओ का जवाब एक बहुत ही अलग दृष्टिकोण लेता है जो वैध भी है, यह हमेशा '' float'' से कास्टिंग करके सभी प्रकार के लिए एक ही कोड का उपयोग करता है जो कम कोड है लेकिन यदि आप बड़े पूर्णांक के साथ काम करना चाहते हैं तो आप एक सीमा में भाग सकते हैं । – Gustavo

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