2017-03-04 9 views
5

पृष्ठभूमि के लिए ले कर जाता है पारित करने के लिए क्या है। Scott's post के बाद मैंने एक दुभाषिया लिखा। मोड़ यह है कि मेरा दुभाषिया सामान्य और पैरामीट्रिज्ड है जो आप इसे खिलाते हैं उसके आधार पर।सबसे अच्छा तरीका सामान्य समारोह है कि कई प्रकार के

परीक्षण प्रयोजनों के लिए मैं में एक और दुभाषिया पारित करने के लिए चाहते हैं, और उसमें रगड़ निहित है - मैं कैसे कर सकते हैं?

let y f = 
    let a = f 1 
    let b = f 2L 
    (a,b) 

f मेरी सामान्य दुभाषिया है, लेकिन यहाँ यह स्पष्ट रूप से int -> 'a करने के लिए पहले प्रयोग के द्वारा विवश है: यहाँ समस्या के सरलीकृत रूपरेखा है। इस सरल परिदृश्य में मैं सिर्फ दो बार दुभाषिया गुजारें सकता, लेकिन मेरी वास्तविक क्रियान्वयन में टाइप अंतरिक्ष बल्कि बड़े (आधार प्रकार x3 उत्पादन प्रकार) है।

वहाँ कुछ एफ # तंत्र है कि मुझे ऐसा करते हैं, w/ओ बहुत अधिक भूमि के ऊपर दिया जाएगा है?

+0

आप एक ही विधि के साथ प्रकारों का उपयोग कर सकते हैं, प्रतिनिधियों के समान कुछ लेकिन स्थिर। अगर मैं चाहता हूं कि मैं आपको कैसे दिखा सकता हूं। दूसरा विकल्प इंटरफेस के साथ है जैसा कि @fyodorsoikin – Gustavo

+0

द्वारा उत्तर में दिखाया गया है आप एक स्थिर प्रकार और/या विधि का उपयोग नहीं कर सकते हैं। इसे तर्क के रूप में पारित करने में सक्षम नहीं होगा। –

+0

हां, आप कर सकते हैं। मैं एक उदाहरण दिखाऊंगा कि कैसे। – Gustavo

उत्तर

6

आप एफ # में ऐसा नहीं कर सकते कार्यों के साथ। मूल्यों के रूप में पारित होने पर कार्य सामान्यता खो देते हैं। इंटरफेस:

हालांकि, एफ # वैसे भी यह कर रही है, थोड़ा awkwardly यद्यपि के लिए एक तंत्र है। इंटरफ़ेस तरीकों, सामान्य हो ताकि आप उन्हें इस्तेमाल के लिए अपने सामान्य कार्यों रैप करने के लिए कर सकते हैं कर सकते हैं:

type Wrapper = 
    abstract member f<'a> : 'a -> 'a 

let y (w: Wrapper) = 
    let a = w.f 1 
    let b = w.f 2L 
    (a, b) 

let genericFn x = x 

// Calling y: 
y { new Wrapper with member __.f x = genericFn x } 

नकारात्मक पक्ष यह है, तो आप वापस उच्च क्रम कार्यों के लिए नहीं जा सकते, ऐसा न हो कि आप genericity खो देते हैं। आपको कछुए तक सभी तरह से इंटरफेस रखना होगा। उदाहरण के लिए, यदि आप एक समारोह के रूप में यह सार संक्षेप द्वारा उदाहरण निर्माण को आसान बनाने में नहीं कर सकते:

let mkWrapper f = 
    // no can do: `f` will be constrained to a non-generic type at this point 
    { new Wrapper with member __.f x = f x } 

लेकिन आप दूसरे पक्ष पर कुछ सुविधा प्रदान कर सकते हैं। कम से कम प्रकार एनोटेशन से छुटकारा पाने:

type Wrapper = abstract member f<'a> (x: 'a): 'a 

let callF (w: Wrapper) x = w.f x 

let y w = 
    let a = callF w 1 
    let b = callF w 2L 
    (a,b) 

(नोट: नाबालिग वाक्यात्मक गलतियों, उपरोक्त कोड में भी हो सकते हैं के रूप में मैं अपने फोन पर लिख रहा हूँ) अगर आप अभी भी रुचि रखते हैं

0

आप कर सकता है कि एक पूर्ण विकसित प्रकार के साथ:

type Function() = 
    member x.DoF<'a> (v:'a) = v 

let y (f: Function) = 
    let a = f.DoF 1 
    let b = f.DoF 2L 
    (a,b) 

y (Function()) 

मैं इसे एफ # में प्रथम श्रेणी के कार्यों के साथ काम करने के लिए एक तरह से पता नहीं है

+1

लेकिन पहली कक्षा के कार्यों का उपयोग करने में असमर्थ होने की बजाय यहां मुख्य समस्या यह है कि आप किसी अन्य फ़ंक्शन पर 'स्विच' करने में सक्षम नहीं होंगे, इसलिए इसे तर्क के रूप में पास करने में कोई बात नहीं है। – Gustavo

2

सुनिश्चित नहीं हैं के बाद से आप पहले से ही एक जवाब स्वीकार किए जाते हैं, लेकिन जैसा कि @Fyodorsoikin यह अनुरोध किया है, यहाँ 'स्थिर' रास्ता नहीं है, यह सब संकलन समय पर होता है, इसलिए कोई क्रम भूमि के ऊपर:

let inline y f = 
    let a = f $ 1 
    let b = f $ 2L 
    (a, b) 

type Double = Double with static member inline ($) (Double, x) = x + x 
type Triple = Triple with static member inline ($) (Triple, x) = x + x + x 

type ToList = ToList with static member  ($) (ToList, x) = [x] 

let res1 = y Double 
let res2 = y Triple 
let res3 = y ToList 

मैं इस तकनीक का प्रयोग जब मैं एक सामान्य की जरूरत है मनमानी संरचनाओं पर कार्य करें, मैं एक विधि 'Invokabl के साथ प्रकारों का नाम देने के लिए उपयोग करता हूं ई '।

अद्यतन

समारोह आप ड्यू में जोड़ने, इस तरह के मानकों को जोड़ने के लिए:

type Print<'a> = Print of 'a with 
    static member inline ($) (Print printer, x) = printer (string x) 

let stdout (x:string) = System.Console.WriteLine x 
let stderr (x:string) = System.Console.Error.WriteLine x 

let res4 = y (Print stdout) 
let res5 = y (Print stderr) 

यह सिर्फ एक त्वरित और आसान नमूना कोड है लेकिन इस दृष्टिकोण परिष्कृत किया जा सकता: आप एक ऑपरेटर के बजाय एक विधि नाम का उपयोग कर सकते हैं, तो आप घोषणा में ड्यू दोहराने के लिए होने से बचने कर सकते हैं, और आप Invokables रचना कर सकते हैं। यदि आप इन वृद्धिओं में रुचि रखते हैं, तो मुझे बताएं।मैंने उत्पादन कोड में पहले इस दृष्टिकोण का परिष्करण किया था और कभी भी कोई समस्या नहीं थी।

+1

अरे, यह धोखा दे रहा है! समारोह इनलाइन है! यह मूल प्रश्न का उत्तर नहीं देगा, और यह भी कम देखता है कि लक्ष्य "निर्भरता इंजेक्शन" कैसे था। –

+0

@FyodorSoikin हां, ठीक है तो आप मुझे एक धोखेबाज डेवलपर कह सकते हैं, क्योंकि जब भी मुझे उनकी ज़रूरत होती है, मैं इनलाइन फ़ंक्शंस का उपयोग करता हूं। – Gustavo

+2

मैं उनका भी उपयोग करता हूं। व्यापक रूप से। उपयोग ही धोखा नहीं है। धोखा देना इस विशिष्ट प्रश्न के संदर्भ में इसका उपयोग करना है। –

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