खोज…
एक से अधिक मापदंडों के कार्य
F # में, सभी फ़ंक्शन ठीक एक पैरामीटर लेते हैं । यह एक अजीब बयान लगता है, क्योंकि यह एक फ़ंक्शन घोषणा में एक से अधिक पैरामीटर घोषित करने के लिए बहुत आसान है:
let add x y = x + y
लेकिन अगर आप F # इंटरएक्टिव इंटरप्रेटर में उस फंक्शन डिक्लेरेशन को टाइप करते हैं, तो आप देखेंगे कि उसका टाइप सिग्नेचर है:
val add : x:int -> y:int -> int
पैरामीटर नामों के बिना, वह हस्ताक्षर int -> int -> int
। ->
ऑपरेटर सही-सहयोगी है, जिसका अर्थ है कि यह हस्ताक्षर int -> (int -> int)
बराबर है। दूसरे शब्दों में, add
एक फ़ंक्शन है जो एक int
पैरामीटर लेता है, और एक फ़ंक्शन देता है जो एक int
ले जाता है और int
रिटर्न करता है । कोशिश करो:
let addTwo = add 2
// val addTwo : (int -> int)
let five = addTwo 3
// val five : int = 5
हालाँकि, आप किसी फ़ंक्शन को add
में अधिक "पारंपरिक" तरीके से कॉल कर सकते हैं, सीधे इसे दो पैरामीटर पास कर सकते हैं, और यह काम करेगा जैसे आप चाहते हैं:
let three = add 1 2
// val three : int = 3
यह उतने ही मापदंडों पर लागू होता है जितना आप चाहते हैं:
let addFiveNumbers a b c d e = a + b + c + d + e
// val addFiveNumbers : a:int -> b:int -> c:int -> d:int -> e:int -> int
let addFourNumbers = addFiveNumbers 0
// val addFourNumbers : (int -> int -> int -> int -> int)
let addThreeNumbers = addFourNumbers 0
// val addThreeNumbers : (int -> int -> int -> int)
let addTwoNumbers = addThreeNumbers 0
// val addTwoNumbers : (int -> int -> int)
let six = addThreeNumbers 1 2 3 // This will calculate 0 + 0 + 1 + 2 + 3
// val six : int = 6
मल्टी-पैरामीटर फ़ंक्शंस के बारे में सोचने का यह तरीका फ़ंक्शंस जो एक पैरामीटर लेता है और नए फ़ंक्शंस देता है (जो बदले में एक पैरामीटर ले सकता है और नए फ़ंक्शंस वापस कर सकता है, जब तक कि आप अंतिम फ़ंक्शन तक नहीं पहुंचते हैं जो अंतिम पैरामीटर लेता है और अंत में एक परिणाम देता है) गणितज्ञ हस्केल करी के सम्मान में, जिसे अवधारणा विकसित करने के लिए प्रसिद्ध है, को करीने कहा जाता है। (इसका आविष्कार किसी और ने किया था, लेकिन करी को इसका श्रेय सबसे अधिक मिलता है।)
इस अवधारणा का उपयोग F # में किया जाता है, और आप इससे परिचित होना चाहते हैं।
कार्यों की मूल बातें
F # में अधिकांश फ़ंक्शन let
सिंटैक्स के साथ बनाए गए हैं:
let timesTwo x = x * 2
यह एक फ़ंक्शन को परिभाषित करता है जिसका नाम timesTwo
जो एकल पैरामीटर x
लेता है। आप एक इंटरैक्टिव एफ # सत्र (चलाते हैं fsharpi
ओएस एक्स और लिनक्स, पर fsi.exe
Windows पर) और में है कि समारोह पेस्ट (और जोड़ने ;;
कि बताता है fsharpi
कोड टाइप करते मूल्यांकन करने के लिए), आप देखेंगे कि यह देखेंगे उत्तर के साथ:
val timesTwo : x:int -> int
इसका मतलब यह है कि timesTwo
एक ऐसा फंक्शन है जो टाइप के int
सिंगल पैरामीटर x
को लेता है, और एक int
लौटाता है। फ़ंक्शन हस्ताक्षर अक्सर पैरामीटर नामों के बिना लिखे जाते हैं, इसलिए आप अक्सर इस फ़ंक्शन प्रकार को int -> int
रूप में लिखा देखेंगे।
लेकिन रुकें! F # को कैसे पता चला कि x
एक पूर्णांक पैरामीटर था, क्योंकि आपने कभी इसका प्रकार निर्दिष्ट नहीं किया था? यह प्रकार के अनुमान के कारण है। क्योंकि फ़ंक्शन बॉडी में, आपने x
को 2
गुणा किया है, x
और 2
के प्रकार समान होने चाहिए। (एक सामान्य नियम के रूप में, F # अलग-अलग प्रकार के मूल्यों को निहित नहीं करेगा; आपको स्पष्ट रूप से किसी भी टाइपकास्ट को निर्दिष्ट करना होगा जो आप चाहते हैं)।
यदि आप कोई ऐसा कार्य बनाना चाहते हैं जो कोई पैरामीटर नहीं लेता है, तो यह गलत तरीका है:
let hello = // This is a value, not a function
printfn "Hello world"
इसे करने का सही तरीका है:
let hello () =
printfn "Hello world"
इस hello
फंक्शन में टाइप unit -> unit
, जिसे "यूनिट" टाइप में समझाया गया है।
करी बनाम टुल्ल किए हुए कार्य
एफ # में कई मापदंडों के साथ फ़ंक्शंस को परिभाषित करने के दो तरीके हैं, करीकृत फ़ंक्शंस और टुपल्ड फ़ंक्शंस।
let curriedAdd x y = x + y // Signature: x:int -> y:int -> int
let tupledAdd (x, y) = x + y // Signature: x:int * y:int -> int
F # के बाहर (जैसे .NET फ्रेमवर्क) से परिभाषित सभी फ़ंक्शन F # में Tupled फॉर्म के साथ उपयोग किए जाते हैं। एफ # कोर मॉड्यूल में अधिकांश फ़ंक्शन का उपयोग करी फॉर्म के साथ किया जाता है।
करी फॉर्म को मुहावरेदार एफ # माना जाता है, क्योंकि यह आंशिक आवेदन के लिए अनुमति देता है। ट्यूप्ड फॉर्म के साथ निम्न में से दो उदाहरण संभव नहीं हैं।
let addTen = curriedAdd 10 // Signature: int -> int
// Or with the Piping operator
3 |> curriedAdd 7 // Evaluates to 10
इसके पीछे कारण यह है कि एक पैरामीटर के साथ कॉल किए जाने पर करी फ़ंक्शन एक फ़ंक्शन देता है। कार्यात्मक प्रोग्रामिंग में आपका स्वागत है !!
let explicitCurriedAdd x = (fun y -> x + y) // Signature: x:int -> y:int -> int
let veryExplicitCurriedAdd = (fun x -> (fun y -> x + y)) // Same signature
आप देख सकते हैं यह वास्तव में एक ही हस्ताक्षर है।
हालाँकि, अन्य .NET कोड के साथ इंटरफेस करते समय, जैसे कि पुस्तकालयों को लिखते समय, ट्यूप्ड फॉर्म का उपयोग करके फ़ंक्शन को परिभाषित करना महत्वपूर्ण है।
इनलाइन
इनलाइनिंग आपको फ़ंक्शन के शरीर के साथ एक फ़ंक्शन को कॉल को बदलने की अनुमति देता है।
यह कभी-कभी कोड के महत्वपूर्ण हिस्से पर प्रदर्शन कारण के लिए उपयोगी होता है। लेकिन प्रतिपक्ष यह है कि आपकी बॉडी बहुत जगह लेगी क्योंकि फंक्शन की बॉडी को हर जगह डुप्लिकेट किया जाता है। किसी फ़ंक्शन को इनलाइन करने या न करने का निर्णय लेते समय आपको सावधान रहना होगा।
एक फ़ंक्शन inline
कीवर्ड के साथ inline
किया जा सकता है:
// inline indicate that we want to replace each call to sayHello with the body
// of the function
let inline sayHello s1 =
sprintf "Hello %s" s1
// Call to sayHello will be replaced with the body of the function
let s = sayHello "Foo"
// After inlining ->
// let s = sprintf "Hello %s" "Foo"
printfn "%s" s
// Output
// "Hello Foo"
स्थानीय मूल्य के साथ एक और उदाहरण:
let inline addAndMulti num1 num2 =
let add = num1 + num2
add * 2
let i = addAndMulti 2 2
// After inlining ->
// let add = 2 + 2
// let i = add * 2
printfn "%i" i
// Output
// 8
पाइप आगे और पीछे
एक सरल और सुरुचिपूर्ण तरीके से फ़ंक्शन को पैरामीटर पास करने के लिए पाइप ऑपरेटरों का उपयोग किया जाता है। यह मध्यवर्ती मूल्यों को खत्म करने और फ़ंक्शन कॉल को पढ़ने के लिए आसान बनाने की अनुमति देता है।
F # में, दो पाइप ऑपरेटर हैं:
फॉरवर्ड (
|>
): बाएं से दाएं से गुजरने वाले पैरामीटरlet print message = printf "%s" message // "Hello World" will be passed as a parameter to the print function "Hello World" |> print
बैकवर्ड (
<|
): दाईं ओर से बाईं ओर पैरामीटर पारित करनाlet print message = printf "%s" message // "Hello World" will be passed as a parameter to the print function print <| "Hello World"
यहाँ पाइप ऑपरेटरों के बिना एक उदाहरण है:
// We want to create a sequence from 0 to 10 then:
// 1 Keep only even numbers
// 2 Multiply them by 2
// 3 Print the number
let mySeq = seq { 0..10 }
let filteredSeq = Seq.filter (fun c -> (c % 2) = 0) mySeq
let mappedSeq = Seq.map ((*) 2) filteredSeq
let finalSeq = Seq.map (sprintf "The value is %i.") mappedSeq
printfn "%A" finalSeq
हम पिछले उदाहरण को छोटा कर सकते हैं और इसे आगे पाइप ऑपरेटर के साथ क्लीनर बना सकते हैं:
// Using forward pipe, we can eliminate intermediate let binding
let finalSeq =
seq { 0..10 }
|> Seq.filter (fun c -> (c % 2) = 0)
|> Seq.map ((*) 2)
|> Seq.map (sprintf "The value is %i.")
printfn "%A" finalSeq
प्रत्येक फ़ंक्शन परिणाम अगले फ़ंक्शन के पैरामीटर के रूप में पारित किया जाता है।
यदि आप पाइप ऑपरेटर को कई पैरामीटर पास करना चाहते हैं, तो आपको एक जोड़ना होगा |
प्रत्येक अतिरिक्त पैरामीटर के लिए और मापदंडों के साथ एक टपल बनाएं। देशी एफ # पाइप ऑपरेटर तीन मापदंडों (|||> या <|||) तक का समर्थन करता है।
let printPerson name age =
printf "My name is %s, I'm %i years old" name age
("Foo", 20) ||> printPerson