'क्लोजर' और 'लैम्ब्डा' में क्या अंतर है?


811

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

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


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



PHP बंद होने के लिए, php.net/manual/en/class.closure.php देखें । यह नहीं है कि एक जावास्क्रिप्ट प्रोग्रामर क्या उम्मीद करेगा।
पॉल एचएच

सासक का जवाब बेहतरीन है। IMHO यह सवाल SO उपयोगकर्ताओं के लिए अधिक उपयोगी होगा यदि यह दर्शकों को उस उत्तर के लिए निर्देशित करता है।
अमिगोनिको

जवाबों:


703

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

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

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

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

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

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

एक और महत्वपूर्ण बिंदु - जब तक इसका मूल्यांकन नहीं किया जा रहा है तब भी यह पर्यावरण के funcऊपर बंद होता रहेगा । यह कोड भी काम करेगा:anotherfuncanotherfunc

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

print anotherfunc(10)()

यह 10 छपेगा।

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


1
क्लाउडीयू, मेरे अनिश्चित ज्ञान के लिए अजगर को कभी भी बंद करने का अधिकार नहीं मिला। जब मैं नहीं देख रहा था तो क्या उन्होंने परिवर्तनशीलता की समस्या को ठीक किया था? बहुत संभव है ...
simon

साइमन - तुम सही हो, वे अभी भी पूरी तरह से सही नहीं हैं। मुझे लगता है कि अजगर 3.0 इस के आसपास काम करने के लिए एक हैक जोड़ देगा। ('नॉनक्लॉक' आइडेंटिफ़ायर जैसा कुछ)। मुझे लगता है कि प्रतिबिंबित करने के लिए मेरे arsr बदल देंगे।
क्लाउडीउ

2
@AlexanderOrlov: वे दोनों लैम्ब्डा और क्लोजर हैं। अनाम आंतरिक कक्षाओं के माध्यम से जावा पहले बंद हो गया था। अब उस कार्यक्षमता को लंबोदर भावों के माध्यम से वाक्य रचना को आसान बना दिया गया है। तो शायद नई सुविधा का सबसे प्रासंगिक पहलू यह है कि अब लैम्ब्डा हैं। उन्हें लंबोदर कहना गलत नहीं है, वे वास्तव में लंबोदर हैं। क्यों जावा 8 लेखक इस तथ्य को उजागर नहीं करने का चयन कर सकते हैं कि वे बंद कर रहे हैं कुछ ऐसा नहीं है जिसे मैं जानता हूं।
क्लाउडीयू

2
@AlexanderOrlov क्योंकि जावा 8 लैम्ब्डा सच्चे क्लोजर नहीं हैं, वे क्लोजर के सिमुलेशन हैं। वे पायथन 2.3 क्लोजर (कोई परिवर्तनशीलता नहीं है, इसलिए 'प्रभावी रूप से अंतिम' होने के लिए संदर्भित चर की आवश्यकता) के समान हैं, और आंतरिक रूप से गैर-क्लोजर कार्यों के लिए संकलित करते हैं जो छिपे हुए मापदंडों के रूप में संलग्न में संदर्भित सभी चर लेते हैं।
लोगन पिकअप

7
@ कोलियडू मुझे लगता है कि एक विशेष भाषा कार्यान्वयन (पायथन) के संदर्भ में उत्तर को उलझाया जा सकता है। सवाल पूरी तरह से भाषा-अज्ञेयवादी है (साथ ही कोई भाषा-विशिष्ट टैग नहीं है)।
मैथ्यू

318

लैम्ब्डा और क्लोजर के आसपास बहुत भ्रम है, यहां तक ​​कि इस स्टैकऑवरफ्लो प्रश्न के उत्तर में भी। यादृच्छिक प्रोग्रामर से पूछने के बजाय जिन्होंने कुछ प्रोग्रामिंग भाषाओं या अन्य क्लूलेस प्रोग्रामर के साथ अभ्यास से बंद होने के बारे में सीखा, स्रोत (जहां यह सब शुरू हुआ) की यात्रा करें। और चूंकि लैम्ब्डा और क्लोजर अलोंजो चर्च द्वारा आविष्कार किए गए लैम्ब्डा कैलकुलस से आते हैं , इसलिए 30 के दशक में पहले इलेक्ट्रॉनिक कंप्यूटर भी मौजूद थे, यही वह स्रोत है जिसके बारे में मैं बात कर रहा हूं।

लैम्ब्डा कैलकुलस दुनिया की सबसे सरल प्रोग्रामिंग भाषा है। केवल एक चीज जो आप इसमें कर सकते हैं: do

  • आवेदन: एक अभिव्यक्ति को दूसरे पर लागू करना, निरूपित करना f x
    (इसे फ़ंक्शन कॉल के रूप में सोचें , फ़ंक्शन कहां fहै और xइसका एकमात्र पैरामीटर है)
  • निर्देश: एक चिन्ह को एक चिह्न में रखने के लिए यह दर्शाता है कि यह प्रतीक सिर्फ एक "स्लॉट" है, एक खाली बॉक्स जो मूल्य से भरा है, एक "चर" जैसा है। यह एक ग्रीक पत्र λ(लैम्ब्डा), फिर प्रतीकात्मक नाम (उदाहरण x), फिर .अभिव्यक्ति से पहले एक बिंदु को जोड़कर किया जाता है। यह तब एक पैरामीटर की अपेक्षा फ़ंक्शन को अभिव्यक्ति में परिवर्तित करता है । उदाहरण के लिए: अभिव्यक्ति लेता है और बताता है कि इस अभिव्यक्ति में प्रतीक एक बाध्य चर है - यह एक मान के साथ प्रतिस्थापित किया जा सकता है जिसे आप एक पैरामीटर के रूप में आपूर्ति करते हैं। ध्यान दें कि फ़ंक्शन इस तरह परिभाषित किया गया है अनाम है
    λx.x+2x+2x
    - इसका कोई नाम नहीं है, इसलिए आप इसे अभी तक संदर्भित नहीं कर सकते हैं, लेकिन आप इसे तुरंत कॉल कर सकते हैं (आवेदन याद है?) इसे उस पैरामीटर की आपूर्ति करके, जिसके लिए यह इंतजार कर रहा है: इस तरह (λx.x+2) 7। तब अभिव्यक्ति (इस मामले में एक शाब्दिक मूल्य) 7को लागू लैम्ब्डा xके उप- रूप में प्रतिस्थापित किया जाता है x+2, इसलिए आपको प्राप्त होता है 7+2, जो तब 9सामान्य अंकगणितीय नियमों द्वारा कम हो जाता है।

इसलिए हम रहस्यों में से एक समाधान कर लिया है:
लैम्ब्डा है गुमनाम समारोह , ऊपर के उदाहरण से λx.x+2


विभिन्न प्रोग्रामिंग भाषाओं में, कार्यात्मक अमूर्त (लैम्ब्डा) के लिए वाक्यविन्यास भिन्न हो सकते हैं। उदाहरण के लिए, जावास्क्रिप्ट में यह इस तरह दिखता है:

function(x) { return x+2; }

और आप इसे इस तरह से कुछ पैरामीटर पर तुरंत लागू कर सकते हैं:

(function(x) { return x+2; })(7)

या आप इस अनाम फ़ंक्शन (लैम्बडा) को कुछ चर में स्टोर कर सकते हैं:

var f = function(x) { return x+2; }

जो प्रभावी रूप से इसे एक नाम देता है f, जिससे आप इसे संदर्भित कर सकते हैं और बाद में कई बार कॉल कर सकते हैं, जैसे:

alert(  f(7) + f(10)  );   // should print 21 in the message box

लेकिन आपको इसे नाम नहीं देना था। आप इसे तुरंत कॉल कर सकते हैं:

alert(  function(x) { return x+2; } (7)  );  // should print 9 in the message box

LISP में, लंबोदर इस तरह से बनाए जाते हैं:

(lambda (x) (+ x 2))

और आप ऐसे लैम्बडा को तुरंत एक पैरामीटर पर लागू करके कॉल कर सकते हैं:

(  (lambda (x) (+ x 2))  7  )


ठीक है, अब दूसरे रहस्य को हल करने का समय है: एक बंद क्या है । ऐसा करने के लिए, आइए हम लैम्ब्डा अभिव्यक्तियों में प्रतीकों ( चर ) के बारे में बात करते हैं ।

जैसा कि मैंने कहा, क्या लैम्ब्डा अमूर्त करता है बाध्यकारी अपने उपसूचक में एक प्रतीक है, इसलिए है कि यह एक substitutible हो जाता है पैरामीटर । इस तरह के प्रतीक को बाध्य कहा जाता है । लेकिन क्या होगा अगर अभिव्यक्ति में अन्य प्रतीक हैं? उदाहरण के लिए λx.x/y+2:। इस अभिव्यक्ति में, प्रतीक xलैंबडा एब्स्ट्रैक्शन से λx.पहले से बंधा हुआ है। लेकिन दूसरा प्रतीक, yबाध्य नहीं है - यह मुफ़्त है । हम नहीं जानते कि यह क्या है और यह कहां से आता है, इसलिए हम यह नहीं जानते कि इसका क्या अर्थ है और यह किस मूल्य का प्रतिनिधित्व करता है, और इसलिए हम उस अभिव्यक्ति का मूल्यांकन नहीं कर सकते जब तक कि हम yइसका क्या मतलब निकालते हैं ।

वास्तव में, वही अन्य दो प्रतीकों के साथ जाता है, 2और +। यह सिर्फ इतना है कि हम इन दो प्रतीकों से इतने परिचित हैं कि हम आमतौर पर भूल जाते हैं कि कंप्यूटर उन्हें नहीं जानता है और हमें यह बताने की आवश्यकता है कि उन्हें कहीं और परिभाषित करने का क्या मतलब है, जैसे कि एक पुस्तकालय या भाषा में।

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

इससे हम लैम्ब्डा के भावों को दो श्रेणियों में विभाजित कर सकते हैं:

  • बंद भाव: इन भावों में होने वाला प्रत्येक प्रतीक किसी न किसी लैंबडा अमूर्तन से बंधा होता है । दूसरे शब्दों में, वे आत्म-निहित हैं ; उन्हें मूल्यांकन के लिए किसी भी आसपास के संदर्भ की आवश्यकता नहीं है। उन्हें कॉम्बिनेटर भी कहा जाता है
  • खुली अभिव्यक्तियाँ: इन अभिव्यक्तियों में कुछ प्रतीक बंधे हुए नहीं हैं - अर्थात् , उनमें से कुछ प्रतीक मुक्त हैं और उन्हें कुछ बाहरी जानकारी की आवश्यकता है, और इस प्रकार उनका मूल्यांकन तब तक नहीं किया जा सकता जब तक आप इन प्रतीकों की परिभाषाओं की आपूर्ति नहीं करते।

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

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

उदाहरण के लिए, यदि आप निम्नलिखित लैम्ब्डा अभिव्यक्ति है: λx.x/y+2, प्रतीक x, स्वाभाविक है, जबकि प्रतीक yनि: शुल्क है, इसलिए अभिव्यक्ति है openऔर मूल्यांकन नहीं किया जा सकता जब तक कि आप कहते हैं कि क्या yअर्थ है (और साथ ही +और 2है, जो भी स्वतंत्र हैं)। लेकिन मान लीजिए कि आपके पास भी ऐसा माहौल है:

{  y: 3,
+: [built-in addition],
2: [built-in number],
q: 42,
w: 5  }

यह पर्यावरण के लिए सभी "अपरिभाषित" (मुक्त) हमारे लैम्ब्डा अभिव्यक्ति से प्रतीकों के लिए आपूर्ति परिभाषाओं ( y, +, 2), और कई अतिरिक्त प्रतीकों ( q, w)। जिन प्रतीकों को हमें परिभाषित करने की आवश्यकता है, वे पर्यावरण के सबसेट हैं:

{  y: 3,
+: [built-in addition],
2: [built-in number]  }

और यह ठीक हमारे लैम्ब्डा अभिव्यक्ति का समापन है:>

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


तो उनसे गलती क्यों हुई? उनमें से बहुत से लोग यह क्यों कहते हैं कि स्मृति में कुछ डेटा संरचनाएं बंद हो जाती हैं, या भाषाओं की कुछ विशेषताएं जो वे उपयोग करती हैं, या वे मेमनों के साथ बंद होने को भ्रमित क्यों करती हैं? : पी

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

और यह इस तथ्य के कारण भी बदतर है कि वे जो कहते हैं, उसमें हमेशा थोड़ी सच्चाई होती है, जो आपको इसे झूठे रूप में आसानी से खारिज करने की अनुमति नहीं देता है: पी मुझे समझाएं:

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

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

Closure {
   [pointer to the lambda function's machine code],
   [pointer to the lambda function's environment]
}

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

जैसा कि मैंने ऊपर बताया, लैम्ब्डा एक्सप्रेशन को बंद करना इसके वातावरण में परिभाषाओं का सबसेट है जो लैम्बडा एक्सप्रेशन में निहित फ्री वैरिएबल्स को वैल्यू देते हैं, एक्सप्रेशन को प्रभावी रूप से बंद करते हैं (एक ओपन लैम्ब्डा एक्सप्रेशन को बदलते हुए, जिसका मूल्यांकन अभी तक नहीं किया जा सकता है, एक बंद लैम्ब्डा अभिव्यक्ति, जिसे तब मूल्यांकन किया जा सकता है, क्योंकि इसमें निहित सभी प्रतीकों को अब परिभाषित किया गया है)।

और कुछ भी नहीं बस एक "कार्गो पंथ" और प्रोग्रामर्स और भाषा विक्रेताओं के "वू-डू जादू" है जो इन धारणाओं की वास्तविक जड़ों से अनजान हैं।

मैं आशा करता हूँ कि यह आपके प्रश्नों का उत्तर देगा। लेकिन अगर आपके पास कोई अनुवर्ती प्रश्न थे, तो उन्हें टिप्पणियों में पूछने के लिए स्वतंत्र महसूस करें, और मैं इसे बेहतर तरीके से समझाने की कोशिश करूंगा।


50
भाषा विशेष के बजाय उदारतापूर्वक चीजों को समझाने वाला सर्वश्रेष्ठ उत्तर
शिशिर अरोड़ा

39
मैं चीजों को समझाते समय इस तरह का दृष्टिकोण पसंद करता हूं। शुरुआत से ही, यह समझाते हुए कि चीजें कैसे काम करती हैं और फिर वर्तमान गलत धारणाएं कैसे बनाई गईं। इस उत्तर को शीर्ष पर जाने की आवश्यकता है।
शार्की

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

3
हालाँकि लैंबडा कैलकुलस मुझे मशीन भाषा की तरह लगता है, लेकिन मुझे यह समझ लेना चाहिए कि यह एक "मिली" भाषा है, जो एक "निर्मित" भाषा के विपरीत है। और इस प्रकार मनमाने सम्मेलनों के अधीन बहुत कम, और वास्तविकता के अंतर्निहित ढांचे को पकड़ने के लिए बहुत अधिक अनुकूल। हम Linq, जावास्क्रिप्ट, एफ # अधिक स्वीकार्य / सुलभ में बारीकियों को पा सकते हैं, लेकिन लैम्ब्डा कैलकुलस बिना किसी व्याकुलता के मामले की नब्ज पर पहुंच जाता है।
स्टीवपॉलिंग

1
मैं सराहना करता हूं कि आपने अपनी बात को कई बार दोहराया, हर बार थोड़ा अलग शब्दों के साथ। यह अवधारणा को सुदृढ़ करने में मदद करता है। काश और लोग ऐसा करते।
जॉनक्लावोर

175

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

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;
           };
 }

14
आप यहाँ किस भाषा में बात कर रहे हैं?
३३:५३

7
यह मूल रूप से स्यूडोकोड है। इसमें कुछ लिस्प और जावास्क्रिप्ट है, साथ ही एक भाषा जिसे मैं "@" ("पर") डिजाइन कर रहा हूं, का नाम चर घोषणा ऑपरेटर के नाम पर रखा गया है।
मार्क सिडैड

3
@MarkCidade, तो यह भाषा कहाँ है @? क्या कोई दस्तावेज और डोनलोड है?
पचेरियर

5
जावास्क्रिप्ट को क्यों न लें और अग्रणी @ चिह्न के साथ चर घोषित करने का अवरोध जोड़ें? इससे समय की थोड़ी बचत होगी :)
नेमोडेन

5
@ स्पेसियर: मैंने भाषा को लागू करना शुरू कर दिया: github.com/marxidad/At2015
मार्क

54

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

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

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

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

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


तो, क्या हम कह सकते हैं कि क्लोजर लैम्ब्डा के एक उपसमुच्चय हैं और लैम्ब्डा कार्यों का एक सबसेट है?
SKER

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

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

मेरी राय में, लंबदा कार्यों की तुलना में अधिक मौलिक अवधारणाएं हैं। यह वास्तव में प्रोग्रामिंग भाषा पर निर्भर करता है।
वी किउ

15

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

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

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


12

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

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

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

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

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

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

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

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

इस पृष्ठभूमि के साथ, इस फ़ंक्शन की जाँच करें:

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

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

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

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

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

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

योग करने के लिए, लैम्ब्डा और क्लोजर वास्तव में अलग-अलग अवधारणाएं हैं। एक लंबोदर एक फ़ंक्शन है। क्लोजर लैम्ब्डा की एक जोड़ी है और संबंधित वातावरण जो लैम्ब्डा को बंद करता है।


तो नेस्टेड लैम्ब्डा द्वारा सभी क्लोजर को तब तक स्थानापन्न किया जा सकता है जब तक कि कोई मुफ्त चर अब मौजूद न हों? इस मामले में मैं कहूंगा कि क्लोजर को विशेष प्रकार के लंबोदे के रूप में देखा जा सकता है।
त्रैलियन

8

कॉन्सेप्ट ऊपर वर्णित के समान है, लेकिन यदि आप 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();

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


7

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

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

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


जावा में बंद करके लामाडास कैसे लागू किया जाता है? क्या इसका मतलब यह है कि लाम्डास की अभिव्यक्ति एक पुरानी शैली के अनाम वर्ग में बदल जाती है?
हैकजुत्सु

5

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


4

एक लैंबडा अभिव्यक्ति केवल एक अनाम फ़ंक्शन है। सादे जावा में, उदाहरण के लिए, आप इसे इस तरह लिख सकते हैं:

Function<Person, Job> mapPersonToJob = new Function<Person, Job>() {
    public Job apply(Person person) {
        Job job = new Job(person.getPersonId(), person.getJobDescription());
        return job;
    }
};

जहाँ क्लास फंक्शन सिर्फ जावा कोड में बनाया गया है। अब आप mapPersonToJob.apply(person)इसे उपयोग करने के लिए कहीं कॉल कर सकते हैं । सिर्फ एक उदाहरण है। इससे पहले कि वहाँ एक वाक्यविन्यास था के लिए वाक्यविन्यास Thats। इसके लिए लैम्ब्डा ने एक शॉर्ट कट किया।

बंद:

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

कोटलिन में, एक लैम्ब्डा हमेशा अपने क्लोजर तक पहुंच सकता है (चर जो इसके बाहरी दायरे में हैं)


0

यह इस बात पर निर्भर करता है कि कोई फ़ंक्शन बाहरी चर का उपयोग करता है या ऑपरेशन करने के लिए नहीं।

बाहरी चर - किसी फ़ंक्शन के दायरे के बाहर परिभाषित चर।

  • लैम्ब्डा के भाव स्टेटलेस हैं क्योंकि यह ऑपरेशन करने के लिए मापदंडों, आंतरिक चर या स्थिरांक पर निर्भर करता है।

    Function<Integer,Integer> lambda = t -> {
        int n = 2
        return t * n 
    }
    
  • क्लोजर राज्य को धारण करते हैं क्योंकि यह ऑपरेशन करने के लिए मापदंडों और स्थिरांक के साथ-साथ बाहरी चर (यानी फ़ंक्शन बॉडी के दायरे के बाहर परिभाषित चर) का उपयोग करता है।

    int n = 2
    
    Function<Integer,Integer> closure = t -> {
        return t * n 
    }
    

जब Java क्लोजर बनाता है, तो यह वेरिएबल n को फंक्शन के साथ रखता है, ताकि इसे अन्य फंक्शन्स में पास किया जा सके या कहीं भी इस्तेमाल किया जा सके।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.