functional-programmingकार्यात्मक-प्रोग्रामिंग के साथ शुरुआत करना


टिप्पणियों

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

कार्यात्मक प्रोग्रामिंग में कुछ दिलचस्प अवधारणाएं हैं:

  • उच्च आदेश कार्य
  • पवित्रता
  • प्रत्यावर्तन
  • आलस्य
  • रेफरेंशियल ट्रांसपेरेंसी
  • Currying
  • functors
  • monads
  • संस्मरण और टेल-कॉल ऑप्टिमाइज़ेशन
  • कार्यात्मक इकाई परीक्षण

कार्यात्मक प्रोग्रामिंग भाषाओं के कुछ उदाहरण लिस्प , हास्केल , स्काला और क्लोजर हैं , लेकिन अन्य भाषाएं भी हैं, जैसे पायथन , आर और जावास्क्रिप्ट एक कार्यात्मक शैली में अपने कार्यक्रमों को (भागों को) लिखने की अनुमति देते हैं। जावा में भी, कार्यात्मक प्रोग्रामिंग ने लैम्बडा एक्सप्रेशंस और स्ट्रीम एपीआई के साथ अपना स्थान पाया है जो जावा 8 में पेश किए गए थे।

Currying

करीकरण एक फ़ंक्शन को बदलने की प्रक्रिया है जो कई तर्कों को कार्यों के अनुक्रम में ले जाती है जिनमें से प्रत्येक में केवल एक ही पैरामीटर होता है। करी से संबंधित है, लेकिन आंशिक अनुप्रयोग के समान नहीं है।

आइए जावास्क्रिप्ट में निम्नलिखित फ़ंक्शन पर विचार करें:

var add = (x, y) => x + y
 

हम फ़ंक्शन को फिर से लिखने के लिए करी की परिभाषा का उपयोग कर सकते हैं:

var add = x => y => x + y
 

यह नया संस्करण एक एकल पैरामीटर, x लेता है, और एक फ़ंक्शन लौटाता है जो एकल पैरामीटर, y लेता है, जो अंततः x और y को जोड़ने का परिणाम लौटाएगा।

var add5 = add(5)
var fifteen = add5(10) // fifteen = 15
 

एक और उदाहरण है जब हमारे पास निम्नलिखित कार्य हैं जो तारों को चारों ओर रखते हैं:

var generalBracket = (prefix, str, suffix) => prefix + str + suffix
 

अब, हर बार जब हम generalBracket उपयोग करते हैं तो हमें कोष्ठक में पास करना होता है:

var bracketedJim = generalBracket("{", "Jim", "}") // "{Jim}"
var doubleBracketedJim = generalBracket("{{", "Jim", "}}") // "{{Jim}}"
 

इसके अलावा, अगर हम उन स्ट्रिंग्स में पास होते हैं जो कोष्ठक नहीं हैं, तो भी हमारा कार्य गलत परिणाम देता है। चलो इसे ठीक करें:

var generalBracket = (prefix, suffix) => str => prefix + str + suffix
var bracket = generalBracket("{", "}")
var doubleBracket = generalBracket("{{", "}}")
 

ध्यान दें कि दोनों bracket और doubleBracket bracket अब अपने अंतिम पैरामीटर की प्रतीक्षा कर रहे हैं:

var bracketedJim = bracket("Jim") // "{Jim}"
var doubleBracketedJim = doubleBracket("Jim") // "{{Jim}}"
 

उच्चतर कार्य

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

ऐसे कार्य जिन्हें न तो इनमें से किया जाता है, को अक्सर first-order functions रूप में जाना जाता है।

function validate(number,predicate) {
    if (predicate) {    // Is Predicate defined
        return predicate(number);
    }
    return false;
}
 

यहाँ "विधेय" एक ऐसा कार्य है जो अपनी दलीलों को शामिल करते हुए कुछ शर्तों के लिए परीक्षण करेगा और सही या गलत लौटेगा।

उपरोक्त के लिए एक उदाहरण कॉल है:

validate(someNumber, function(arg) {
    return arg % 10 == 0;
    }
);
 

एक आम आवश्यकता एक सीमा के भीतर संख्याओं को जोड़ना है। उच्च-क्रम के कार्यों का उपयोग करके हम इस मूल क्षमता को बढ़ा सकते हैं, योग में इसे शामिल करने से पहले प्रत्येक संख्या पर एक परिवर्तन फ़ंक्शन लागू कर सकते हैं।

आप किसी दिए गए रेंज में सभी पूर्णांक जोड़ना चाहते हैं (स्काला का उपयोग करके)

def sumOfInts(a: Int, b: Int): Int = {
  if(a > b) 0
  else a + sumOfInts(a+1, b)
}
 

आप किसी पूर्ण श्रेणी के सभी पूर्णांकों को जोड़ना चाहते हैं

def square(a: Int): Int = a * a

def sumOfSquares(a: Int, b: Int): Int = {
  if(a > b) 0
  else square(a) + sumOfSquares(a + 1, b)
}
 

ध्यान दें कि इन चीजों में 1 बात सामान्य है, कि आप प्रत्येक तर्क पर एक फ़ंक्शन लागू करना चाहते हैं और फिर उन्हें जोड़ सकते हैं।

दोनों करने के लिए एक उच्च-क्रम फ़ंक्शन बनाएँ:

def sumHOF(f: Int => Int, a: Int, b: Int): Int = {
  if(a > b) 0
  else f(a) + sumHOF(f, a + 1, b)
}
 

आप इसे इस तरह कह सकते हैं:

def identity(a: Int): Int = a

def square(a: Int): Int = a * a
 

ध्यान दें कि sumOfInts और sumOfSquare को इस प्रकार परिभाषित किया जा सकता है:

def sumOfInts(a: Int, b: Int): Int = sumHOF(identity, a, b)

def sumOfSquares(a: Int, b: Int): Int = sumHOF(square, a, b)
 

जैसा कि आप इस सरल उदाहरण से देख सकते हैं, उच्च-क्रम फ़ंक्शन अधिक सामान्यीकृत समाधान प्रदान करते हैं और कोड दोहराव को कम करते हैं।

मैंने एक संदर्भ के रूप में मार्टिन ओडस्की द्वारा स्केला बाय उदाहरण का उपयोग किया है।

अचल स्थिति

पारंपरिक वस्तु-उन्मुख भाषाओं में, x = x + 1 एक सरल और कानूनी अभिव्यक्ति है। लेकिन कार्यात्मक प्रोग्रामिंग में, यह अवैध है

क्रियात्मक प्रोग्रामिंग में चर मौजूद नहीं हैं। संग्रहीत मूल्यों को अभी भी केवल इतिहास के कारण चर कहा जाता है। वास्तव में, वे निरंतर हैं । एक बार x मान लेने के बाद, यह जीवन के लिए मूल्य है।

तो, यदि एक चर स्थिर है , तो हम इसके मूल्य को कैसे बदल सकते हैं?

कार्यात्मक प्रोग्रामिंग एक रिकॉर्ड में मूल्यों के परिवर्तन के साथ रिकॉर्ड की एक प्रति बनाकर मूल्यों को बदल देता है।

उदाहरण के लिए, करने के बजाय:

var numbers = [1, 2, 3];
numbers[0] += 1; // numbers = [2, 2, 3];
 

तुम करो:

var numbers = [1, 2, 3];
var newNumbers = numbers.map(function(number) {
    if (numbers.indexOf(number) == 0)
        return number + 1
    return number
});
console.log(newNumbers) // prints [2, 2, 3]
 

और फंक्शनल प्रोग्रामिंग में कोई लूप नहीं हैं। हम जैसे प्रत्यावर्तन या उच्च क्रम कार्यों का उपयोग map , filter और reduce पाशन से बचने के लिए।

आइए जावास्क्रिप्ट में एक सरल लूप बनाएं:

var acc = 0;
for (var i = 1; i <= 10; ++i)
    acc += i;
console.log(acc); // prints 55
 

हम अभी भी acc के जीवनकाल को वैश्विक से स्थानीय में बदलकर बेहतर कर सकते हैं:

function sumRange(start, end, acc) {
    if (start > end)
        return acc;
    return sumRange(start + 1, end, acc + start)
}
console.log(sumRange(1, 10, 0)); // 55
 

कोई चर या लूप का मतलब सरल, सुरक्षित और अधिक पठनीय कोड नहीं है (विशेषकर जब डिबगिंग या परीक्षण - आपको कई बयानों के बाद x के मूल्य के बारे में चिंता करने की आवश्यकता नहीं है, यह कभी नहीं बदलेगा)।

शुद्ध कार्य

शुद्ध कार्य स्व-निहित हैं, और इसका कोई दुष्प्रभाव नहीं है। इनपुट के समान सेट को देखते हुए, एक शुद्ध फ़ंक्शन हमेशा समान आउटपुट मान लौटाएगा।

निम्न फ़ंक्शन शुद्ध है:

function pure(data) {
    return data.total + 3;
}
 

हालाँकि, यह फ़ंक्शन शुद्ध नहीं है क्योंकि यह एक बाहरी चर को संशोधित करता है:

function impure(data) {
    data.total += 3;
    return data.total;
}
 

उदाहरण:

data = {
    total: 6
};

pure(data);   // outputs: 9
impure(data); // outputs: 9 (but now data.total has changed)
impure(data); // outputs: 12