2010-04-27 25 views
19

बुला समारोह मैं कोड का एक टुकड़ा है। मैं धारणा है कि इन दो फ़ंक्शन कॉल आपस में बदला जा तहत किया गया था:एफ # वाक्य रचना भ्रम

f (a, b) 
(f a b) 

त्रुटि है कि मैं मिलता है:

सदस्य या वस्तु निर्माता 'GetAttributeValue' 2 तर्कों लेने के लिए इस कोड से सुलभ नहीं हैं स्थान। विधि 'GetAttributeValue' के सभी सुलभ संस्करण 2 तर्क लेते हैं।

जो मनोरंजक प्रतीत होता है, जैसा कि ऐसा लगता है कि मुझे इसकी आवश्यकता है। मुझे यहां क्या समझ नहीं आ रहा है?

उत्तर

53

एफ # में एक सामान्य समारोह कॉल कोष्ठकों के बिना लिखा है और मानकों रिक्तियों से अलग कर रहे हैं। कई मापदंडों के एक समारोह को परिभाषित करने का आसान तरीका यह लिखना है:

let add a b = a + b 

के रूप में पास्कल बताया गया है, को निर्दिष्ट मापदंडों का इस तरह से currying कहा जाता है - विचार एक समारोह सिर्फ एक पैरामीटर लेता है कि है और परिणाम है एक ऐसा फ़ंक्शन जो दूसरा पैरामीटर लेता है और वास्तविक परिणाम (या कोई अन्य फ़ंक्शन) देता है। इस तरह के एक साधारण समारोह को कॉल करते समय, आप add 10 5 लिखेंगे और संकलक (सिद्धांत रूप में) इसे ((add 10) 5) के रूप में व्याख्या करता है। यह कुछ अच्छा लाभ है - उदाहरण के लिए यह आप का उपयोग करने की अनुमति देता आंशिक समारोह आवेदन जहाँ आप एक समारोह का केवल एक पहले कुछ तर्क निर्दिष्ट करें:

let addTen = add 10 // declares function that adds 10 to any argument 
addTen 5 // returns 15 
addTen 9 // returns 19 

जब सूचियों प्रसंस्करण यह सुविधा उदाहरण के लिए व्यावहारिक रूप से उपयोगी है:

// The code using explicit lambda functions.. 
[ 1 .. 10 ] |> List.map (fun x -> add 10 x) 

// Can be rewritten using partial function application: 
[ 1 .. 10 ] |> List.map (add 10) 

अब, भ्रमित भाग में जाएं - एफ # में, आप टुपल्स के साथ भी काम कर सकते हैं, जो कि सरल डेटा प्रकार हैं जो आपको कई मानों को एक ही मान में समूहित करने की अनुमति देते हैं (ध्यान दें कि टुपल्स फ़ंक्शंस से संबंधित नहीं हैं किसी भी प्रकार)।उदाहरण के लिए आप लिख सकते हैं:

// The following function: 
let add (a, b) = a * b 

// ...means exactly the same thing as: 
let add tup = 
    let (a, b) = tup // extract elements of a tuple 
    a * b 

// You can call the function by creating tuple inline: 
add (10, 5) 
// .. or by creating tuple in advance 
let t = (10, 5) 
add t 
:

let tup = (10, "ten") // creating a tuple 
let (n, s) = tup   // extracting elements of a tuple using pattern 
printfn "n=%d s=%s" n s // prints "n=10 s=ten" 

यदि आप एक समारोह है कि एक अल्पविराम के द्वारा अलग कोष्ठक में पैरामीटर लेता है लिखते हैं, आप वास्तव में एक समारोह है कि एक एकल पैरामीटर जो एक टपल है लेता लिख ​​रहे हैं

यह एक अलग प्रकार का एक कार्य है - यह एक एकल पैरामीटर लेता है जो एक ट्यूपल होता है, जबकि पहला संस्करण एक ऐसा फ़ंक्शन था जिसमें दो पैरामीटर (करी का उपयोग करके) लिया गया था।

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

हालांकि, सिद्धांत यह है कि एक फ़ंक्शन या तो एकाधिक पैरामीटर लेता है या पैरामीटर के रूप में एक ट्यूपल लेता है।

+6

ग्रेट उत्तर, लेकिन ध्यान दें कि आपका दूसरा-से-अंतिम पैराग्राफ बिल्कुल सही नहीं है - एक गैर-सिंटैक्टिक ट्यूपल को .NET विधि में पास करना पूरी तरह से संभव है (उदाहरण के लिए 'ऑब्जेक्ट में टी = 1,2 ऑब्जेक्ट। ReeferenceEquals t') । यह अधिभारित तरीकों के लिए काम नहीं करता है, हालांकि। – kvb

+1

@kvb: अच्छा बिंदु - मुझे पता था कि कुछ सीमाएं थीं, लेकिन मुझे बिल्कुल यकीन नहीं था। स्पष्टीकरण के लिए धन्यवाद। –

+3

बहुत बढ़िया! इस तरह के जवाब स्टैक ओवरव्लो को इतनी मूल्यवान जगह बनाते हैं। धन्यवाद थॉमस। – Daniel

3

f (a,b) में, f एक ऐसा कार्य होना चाहिए जो एक तर्क (जो एक जोड़ी है) लेता हो।

f a b में, जो (f a) b, f के लिए कम जब a रिटर्न एक कार्य है जो b लिए आवेदन किया है के लिए आवेदन किया है।

दोनों फ़ंक्शन में तर्क पारित करने के लगभग समकक्ष तरीके हैं, लेकिन आप एक शैली के लिए एक शैली के लिए डिज़ाइन किए गए फ़ंक्शन का उपयोग नहीं कर सकते हैं। दूसरी शैली को "currying" कहा जाता है। a जैसे ही कुछ कंप्यूटेशंस बनने की अनुमति देने का लाभ है, खासकर यदि आप a का उपयोग b एस के साथ करने जा रहे हैं। इस मामले में तुम लिख सकते हैं:

let f_a = f a (* computations happen now that a is available *) 
in 
f_a b1 .... f_a b2 .... 
+0

तो (fab) किसी अन्य के लिए काम नहीं करेगा। नेट लैंग फ़ंक्शन, क्योंकि मुझे विश्वास नहीं है कि उनमें से कोई भी करीबी होने के लिए बनाया गया है? – Daniel

+0

@ डैनियल: मुझे संदेह होगा कि यदि आप F # में फ़ंक्शन 'एफ ए बी' को परिभाषित करते हैं, तो आप इसे 'f (a) (b)' के रूप में कॉल करने में सक्षम होंगे। सी # (जैसा कि वास्तव में 'एफ बी' का अर्थ है पास्कल ने बताया)। – sepp2k

+0

@ sepp2k: मेरा मतलब है कि मूल रूप से सी #, वीबी में बनाए गए कार्यों के लिए सभी कॉल, और हो सकता है कि कुछ अन्य .net लैंग फॉर्म एफ (ए, बी) के रूप में हैं, क्योंकि उन अन्य भाषाओं में करी का समर्थन नहीं है। – Daniel

2

अपने निहित सवाल का जवाब देने, स्थिति इस तरह का में, यह एक छोटे सहायक समारोह लिखने के लिए उपयोगी हो सकता है:

let getAttrVal (x:TypeOfX) key default = x.GetAttributeValue(key, default) 

//usage 
links |> Seq.map (fun x -> getAttrVal x "href", "no url")) 

और आप इसे कैसे उपयोग करना चाहते हैं पर निर्भर करता है, यह करने के लिए और अधिक उपयोगी हो सकता है करी इसे 'पिछड़ा':

let getAttrVal key default (x:TypeOfX) = x.GetAttributeValue(key, default) 

//partial application 
let getHRef = getAttrVal "href" "no url" 

//usage 
links |> Seq.map (fun x -> getHRef x) 

//or, same thing: 
links |> Seq.map getHRef 
संबंधित मुद्दे