function - 'बंद' और 'लैम्ब्डा' के बीच क्या अंतर है?




lambda functional-programming (7)

क्या कोई समझा सकता है? मैं उनके पीछे मूल अवधारणाओं को समझता हूं लेकिन मैं अक्सर उन्हें एक दूसरे के लिए इस्तेमाल किया जाता हूं और मुझे भ्रमित हो जाता है।

और अब जब हम यहां हैं, वे नियमित कार्य से अलग कैसे होते हैं?


अवधारणा ऊपर वर्णित जैसा ही है, लेकिन यदि आप PHP पृष्ठभूमि से हैं, तो यह PHP कोड का उपयोग करके समझाएगा।

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, function ($v) { return $v > 2; });

फ़ंक्शन ($ v) {वापसी $ v> 2; } लैम्ब्डा फ़ंक्शन परिभाषा है। हम इसे एक चर में भी स्टोर कर सकते हैं, इसलिए इसे पुन: प्रयोज्य किया जा सकता है:

$max = function ($v) { return $v > 2; };

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max);

अब, अगर आप फ़िल्टर किए गए सरणी में अधिकतम संख्या को बदलना चाहते हैं तो क्या होगा? आपको एक और लैम्ब्डा फ़ंक्शन लिखना होगा या बंद करना होगा (PHP 5.3):

$max_comp = function ($max) {
  return function ($v) use ($max) { return $v > $max; };
};

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max_comp(2));

एक बंद एक ऐसा कार्य होता है जिसका मूल्यांकन अपने पर्यावरण में किया जाता है, जिसमें एक या अधिक बाध्य चर होते हैं जिन्हें फ़ंक्शन कहा जाता है। वे कार्यात्मक प्रोग्रामिंग दुनिया से आते हैं, जहां खेल में कई अवधारणाएं हैं। बंदरगाह लैम्ब्डा कार्यों की तरह हैं, लेकिन इस अर्थ में समझदार है कि उनके पास बाहरी वातावरण से चर के साथ बातचीत करने की क्षमता है जहां बंद परिभाषित किया गया है।

PHP बंद करने का एक सरल उदाहरण यहां दिया गया है:

$string = "Hello World!";
$closure = function() use ($string) { echo $string; };

$closure();

इस आलेख में अच्छी तरह से समझाया गया।


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

एक बंद करना कोई भी कार्य है जो पर्यावरण को बंद करता है जिसमें इसे परिभाषित किया गया था। इसका अर्थ यह है कि यह चरम सीमा तक नहीं पहुंच सकता है इसकी पैरामीटर सूची में नहीं। उदाहरण:

def func(): return h
def anotherfunc(h):
   return func()

यह एक त्रुटि का कारण बन जाएगा, क्योंकि func किसी anotherfunc में पर्यावरण पर बंद नहीं होता है - h अपरिभाषित है। func केवल वैश्विक पर्यावरण पर बंद हो जाता है। यह काम करेगा:

def anotherfunc(h):
    def func(): return h
    return func()

क्योंकि यहां, func को anotherfunc में परिभाषित किया anotherfunc , और अजगर 2.3 और अधिक (या इस तरह की कुछ संख्या) में जब वे लगभग बंद हो जाते हैं (उत्परिवर्तन अभी भी काम नहीं करता है), इसका मतलब है कि यह किसी anotherfunc के पर्यावरण पर बंद हो जाता है और पहुंच सकता है इसके अंदर चर। पायथन 3.1+ में, nonlocal कीवर्ड का उपयोग करते समय उत्परिवर्तन भी काम करता है।

एक अन्य महत्वपूर्ण बात - func किसी anotherfunc के पर्यावरण को बंद करना जारी रखेगा, भले ही इसका अब किसी anotherfunc में मूल्यांकन नहीं किया जा रहा हो। यह कोड भी काम करेगा:

def anotherfunc(h):
    def func(): return h
    return func

print anotherfunc(10)()

यह 10 प्रिंट करेगा।

जैसा कि आप देखते हैं, लैम्ब्डा के साथ कुछ लेना देना नहीं है - वे दो अलग-अलग (हालांकि संबंधित) अवधारणाएं हैं।


जब अधिकांश लोग कार्य के बारे में सोचते हैं, तो वे नामित कार्यों के बारे में सोचते हैं:

function foo() { return "This string is returned from the 'foo' function"; }

इन्हें नाम से बुलाया जाता है, ज़ाहिर है:

foo(); //returns the string above

लैम्ब्डा अभिव्यक्तियों के साथ, आप अज्ञात कार्य कर सकते हैं:

 @foo = lambda() {return "This is returned from a function without a name";}

उपरोक्त उदाहरण के साथ, आप लैम्ब्डा को वेरिएबल के माध्यम से कॉल कर सकते हैं जिसे इसे असाइन किया गया था:

foo();

चर के लिए अज्ञात कार्यों को असाइन करने से अधिक उपयोगी, हालांकि, उन्हें उच्च-कार्य फ़ंक्शंस से या उससे गुजर रहे हैं, यानी, जो फ़ंक्शन अन्य कार्यों को स्वीकार / वापस करते हैं। इन मामलों में से कई में, फ़ंक्शन नामकरण अनावश्यक है:

function filter(list, predicate) 
 { @filteredList = [];
   for-each (@x in list) if (predicate(x)) filteredList.add(x);
   return filteredList;
 }

//filter for even numbers
filter([0,1,2,3,4,5,6], lambda(x) {return (x mod 2 == 0)}); 

एक बंद नाम या अज्ञात फ़ंक्शन हो सकता है, लेकिन इस तरह के रूप में जाना जाता है जब यह उस क्षेत्र में चर "बंद हो जाता है" जहां कार्य परिभाषित किया जाता है, यानी, बंद होने पर पर्यावरण को किसी भी बाहरी चर के साथ संदर्भित किया जाएगा जिसका उपयोग किया जाता है खुद को बंद करो। यहां एक नाम बंद है:

@x = 0;

function incrementX() { x = x + 1;}

incrementX(); // x now equals 1

यह बहुत अधिक प्रतीत नहीं होता है लेकिन क्या होगा यदि यह सब किसी अन्य कार्य में था और आपने बाहरी फ़ंक्शन में incrementX पारित किया था?

function foo()
 { @x = 0;

   function incrementX() 
    { x = x + 1;
      return x;
    }

   return incrementX;
 }

@y = foo(); // y = closure of incrementX over foo.x
y(); //returns 1 (y.x == 0 + 1)
y(); //returns 2 (y.x == 1 + 1)

इस तरह आप कार्यात्मक प्रोग्रामिंग में राज्य की वस्तुओं को प्राप्त करते हैं। चूंकि "incrementX" नामकरण की आवश्यकता नहीं है, इसलिए आप इस मामले में लैम्ब्डा का उपयोग कर सकते हैं:

function foo()
 { @x = 0;

   return lambda() 
           { x = x + 1;
             return x;
           };
 }

प्रोग्रामिंग भाषाओं के दृष्टिकोण से, वे पूरी तरह से दो अलग-अलग चीजें हैं।

मूल रूप से एक ट्यूरिंग पूर्ण भाषा के लिए हमें केवल सीमित तत्वों की आवश्यकता होती है, जैसे अमूर्तता, आवेदन और कमी। एब्स्ट्रक्शन और एप्लिकेशन जिस तरह से आप लैम्ब्बा अभिव्यक्ति का निर्माण कर सकते हैं, और लैम्ब्डा अभिव्यक्ति का अर्थ डर्टरमाइन को कम कर देता है।

लैम्ब्डा एक तरीका प्रदान करता है जिसे आप गणना प्रक्रिया को सार कर सकते हैं। उदाहरण के लिए, दो संख्याओं की गणना करने के लिए, एक प्रक्रिया जो दो पैरामीटर x, y और रिटर्न x + y लेती है, को समझा जा सकता है। योजना में, आप इसे लिख सकते हैं

(lambda (x y) (+ x y))

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

ठीक है, अब कल्पना करें कि इसे कैसे कार्यान्वित किया जा सकता है। जब भी हम कुछ अभिव्यक्तियों के लिए लैम्ब्डा अभिव्यक्ति लागू करते हैं, उदाहरण के लिए

((lambda (x y) (+ x y)) 2 3)

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

इस पृष्ठभूमि के साथ, इस समारोह की जांच करें:

(lambda (x y) (+ x y z))

हम जानते हैं कि जब हम लैम्ब्डा अभिव्यक्ति का मूल्यांकन करते हैं, तो xy एक नई तालिका में बंधेगा। लेकिन हम कैसे और कहाँ ज़ेड अप देख सकते हैं? असल में जेड को एक फ्री वैरिएबल कहा जाता है। एक बाहरी वातावरण होना चाहिए जिसमें ज़ेड शामिल हो। अन्यथा अभिव्यक्ति का अर्थ केवल बाध्यकारी एक्स और वाई द्वारा निर्धारित नहीं किया जा सकता है। यह स्पष्ट करने के लिए, आप योजना में निम्नानुसार कुछ लिख सकते हैं:

((lambda (z) (lambda (x y) (+ x y z))) 1)

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

यह ऐसा कुछ है जिसे आप पहले से ही जानते हैं, वस्तुओं की एक विधि लगभग हमेशा वस्तुओं की स्थिति पर निर्भर करती है। यही कारण है कि कुछ लोग कहते हैं कि "बंदरगाह गरीब व्यक्ति की वस्तुएं हैं।" लेकिन हम वस्तुओं को गरीब व्यक्ति के बंद होने के रूप में भी मान सकते हैं क्योंकि हम वास्तव में प्रथम श्रेणी के कार्यों को पसंद करते हैं।

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

संक्षेप में, लैम्ब्डा और बंद वास्तव में अलग-अलग अवधारणाएं हैं। एक लैम्ब्डा एक समारोह है। एक बंद लैम्ब्डा की एक जोड़ी है और इसी वातावरण जो लैम्ब्डा को बंद करता है।


यह उतना ही सरल है: लैम्ब्डा एक भाषा निर्माण है, यानि अज्ञात कार्यों के लिए सिंटैक्स; एक बंद करना इसे लागू करने के लिए एक तकनीक है - या किसी भी प्रथम श्रेणी के कार्यों, उस मामले के लिए, नाम या अज्ञात।

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

दुर्भाग्यवश, वहां कई भाषाएं हैं जो प्रथम श्रेणी के मूल्यों के रूप में कार्यों का समर्थन नहीं करती हैं, या केवल अपंग रूप में उनका समर्थन करती हैं। इसलिए लोग अक्सर "वास्तविक चीज़" को अलग करने के लिए "बंद" शब्द का उपयोग करते हैं।


यह सवाल पुराना है और कई जवाब मिल गए हैं। अब जावा 8 और आधिकारिक लैम्ब्डा के साथ जो अनौपचारिक बंद परियोजनाएं हैं, यह सवाल को पुनर्जीवित करता है।

जावा संदर्भ में जवाब ( Lambdas और बंद के माध्यम से - क्या अंतर है? ):

"एक बंदरगाह एक लैम्ब्डा अभिव्यक्ति है जो पर्यावरण के साथ जोड़ा जाता है जो अपने प्रत्येक मुक्त चर को मूल्य में जोड़ता है। जावा में, लैम्ब्डा अभिव्यक्ति बंद होने के माध्यम से लागू की जाएगी, इसलिए दोनों शब्दों को समुदाय में एक दूसरे के रूप में उपयोग किया जाएगा।"


सभी बंद नहीं हैं lambdas और सभी lambdas बंद नहीं हैं। दोनों कार्य हैं, लेकिन जरूरी नहीं कि जिस तरह से हम जानना चाहते हैं।

एक लैम्ब्डा अनिवार्य रूप से एक ऐसा कार्य है जिसे कार्यों को घोषित करने की मानक विधि के बजाय इनलाइन परिभाषित किया जाता है। Lambdas अक्सर वस्तुओं के रूप में चारों ओर पारित किया जा सकता है।

एक बंद करना एक ऐसा कार्य है जो अपने शरीर के बाहरी क्षेत्रों को संदर्भित करके अपने आसपास के राज्य को घेरता है। संलग्न राज्य बंद होने के आक्रमणों में रहता है।

ऑब्जेक्ट-ओरिएंटेड भाषा में, आमतौर पर बंद वस्तुओं को ऑब्जेक्ट्स के माध्यम से प्रदान किया जाता है। हालांकि, कुछ ओओ भाषाएं (उदाहरण के लिए सी #) विशेष कार्यक्षमता को कार्यान्वित करती हैं जो पूरी तरह से कार्यात्मक भाषाओं (जैसे लिस्प) द्वारा प्रदान किए गए बंद होने की परिभाषा के करीब होती है जिसमें राज्य को घेरने के लिए ऑब्जेक्ट नहीं होते हैं।

दिलचस्प बात यह है कि सी # में लैम्बडास और क्लोजर का परिचय मुख्यधारा के उपयोग के करीब कार्यात्मक प्रोग्रामिंग लाता है।





closures