एक बंद क्या है?


155

हर अब और फिर मुझे "क्लोजर" का उल्लेख किया जा रहा है, और मैंने इसे देखने की कोशिश की, लेकिन विकी एक स्पष्टीकरण नहीं देता है जिसे मैं समझता हूं। क्या कोई मेरी मदद कर सकता है?


यदि आप जावा / सी # जानते हैं तो उम्मीद है कि यह लिंक मदद करेगा- http://www.developerfusion.com/article/8251/the-beauty-of-closures/
गुलशन

1
क्लोजर को समझना मुश्किल है। आपको उस विकिपीडिया लेख के पहले वाक्य में सभी लिंक पर क्लिक करने की कोशिश करनी चाहिए, और पहले उन लेखों को समझना चाहिए।
Zach


3
हालांकि एक बंद और एक वर्ग के बीच मौलिक अंतर क्या है? ठीक है, केवल एक सार्वजनिक पद्धति वाला एक वर्ग।
biziclop

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

जवाबों:


141

(डिस्क्लेमर: यह एक बुनियादी व्याख्या है; जहाँ तक परिभाषा जाती है, मैं थोड़ा सरल कर रहा हूँ)

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

उदाहरण (जावास्क्रिप्ट):

var setKeyPress = function(callback) {
    document.onkeypress = callback;
};

var initialize = function() {
    var black = false;

    document.onclick = function() {
        black = !black;
        document.body.style.backgroundColor = black ? "#000000" : "transparent";
    }

    var displayValOfBlack = function() {
        alert(black);
    }

    setKeyPress(displayValOfBlack);
};

initialize();

कार्य 1 को सौंपा गया है document.onclickऔर displayValOfBlackबंद हैं। आप देख सकते हैं कि वे दोनों बूलियन चर का संदर्भ देते हैं black, लेकिन यह चर फ़ंक्शन के बाहर सौंपा गया है। क्योंकि blackहै गुंजाइश जहां समारोह परिभाषित किया गया था करने के लिए स्थानीय , इस चर के लिए सूचक संरक्षित है।

यदि आप इसे HTML पृष्ठ में रखते हैं:

  1. ब्लैक में बदलने के लिए क्लिक करें
  2. हिट [दर्ज] "सच" देखने के लिए
  3. फिर से क्लिक करें, वापस सफेद रंग में बदल जाता है
  4. हिट [दर्ज करें] "झूठी" देखने के लिए

यह दर्शाता है कि दोनों की पहुंच समान है black , और इसका उपयोग बिना किसी आवरण वस्तु के राज्य को संग्रहीत करने के लिए किया जा सकता है ।

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

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


1: वास्तव में, जावास्क्रिप्ट में सभी फ़ंक्शन क्लोजर हैं।


3
जैसा कि मैं आपके उत्तर को पढ़ रहा था, मुझे लगा कि मेरे दिमाग में एक हल्का-फुल्का मोड़ आ गया है। बहुत सराहना की! :)
जे

1
चूंकि blackएक फ़ंक्शन के अंदर घोषित किया गया है, इसलिए क्या यह स्टैक अनइंड्स के रूप में नष्ट नहीं होगा ...?
गैब्लिन

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

2
@ गैब्लिन, यह एक अच्छा सवाल है। मुझे नहीं लगता कि वे & mdash नहीं कर सकते ; लेकिन मैं केवल कचरा संग्रह लाया, क्योंकि जेएस का उपयोग करता है और यही आप के लिए कहा जा रहा है जब आपने कहा था "जब blackसे एक समारोह के अंदर घोषित किया जाता है, तब तक वह नष्ट नहीं होगा"। यह भी याद रखें कि यदि आप किसी फ़ंक्शन में किसी ऑब्जेक्ट की घोषणा करते हैं और फिर उसे एक ऐसे वेरिएबल को असाइन करते हैं जो कहीं और रहता है, तो वह ऑब्जेक्ट संरक्षित है क्योंकि इसके अन्य संदर्भ हैं।
निकोल

1
ऑब्जेक्टिव-सी (और क्लैंग के तहत) ब्लॉकों का समर्थन करता है, जो अनिवार्य रूप से बंद होते हैं, कचरा संग्रह के बिना। इसके लिए रनटाइम समर्थन और मेमोरी प्रबंधन के आसपास कुछ मैनुअल हस्तक्षेप की आवश्यकता होती है।
क्विक्सोटो

68

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

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

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


3
+1: अच्छा जवाब। आप एक बंद वस्तु को केवल एक विधि के साथ देख सकते हैं, और कुछ सामान्य अंतर्निहित डेटा (ऑब्जेक्ट के सदस्य चर) पर क्लोजर के संग्रह के रूप में एक मनमाना वस्तु। मुझे लगता है कि ये दोनों विचार काफी सममित हैं।
जियोर्जियो

3
बहुत अच्छा जवाब। यह वास्तव में बंद करने की अंतर्दृष्टि की व्याख्या करता है।
रोबोएलेक्स

1
@ मेसन व्हीलर: क्लोजर डेटा कहां संग्रहीत किया जाता है? एक समारोह की तरह ढेर में? या एक वस्तु की तरह ढेर में?
रोबोएलेक्स

1
@RoboAlex: ढेर में, क्योंकि यह एक वस्तु है जो एक फ़ंक्शन की तरह दिखता है
मेसन व्हीलर

1
@RoboAlex: जहां एक क्लोजर और उसके कैप्चर किए गए डेटा को स्टोर किया जाता है, कार्यान्वयन पर निर्भर करता है। सी ++ में इसे ढेर या ढेर पर संग्रहीत किया जा सकता है।
जियोर्जियो

29

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

उदाहरण के लिए स्कैला फ़ंक्शन परिभाषा लें:

def addConstant(v: Int): Int = v + k

फ़ंक्शन बॉडी में दो नाम (चर) होते हैं vऔर kदो पूर्णांक मानों का संकेत देते हैं। नाम vबाध्य है क्योंकि यह फ़ंक्शन के एक तर्क के रूप में घोषित किया गया है addConstant(फ़ंक्शन घोषणा को देखकर हम जानते हैं कि vफ़ंक्शन को लागू किए जाने पर एक मान असाइन किया जाएगा)। नाम kनि: शुल्क wrt फ़ंक्शन है addConstantक्योंकि फ़ंक्शन में कोई सुराग नहीं है कि मूल्य क्या k(और कैसे) से जुड़ा हुआ है।

कॉल का मूल्यांकन करने के लिए जैसे:

val n = addConstant(10)

हमें kएक मान निर्दिष्ट करना होगा, जो केवल तभी हो सकता है जब नाम kउस संदर्भ में परिभाषित किया गया हो जिसमें addConstantपरिभाषित किया गया हो। उदाहरण के लिए:

def increaseAll(values: List[Int]): List[Int] =
{
  val k = 2

  def addConstant(v: Int): Int = v + k

  values.map(addConstant)
}

अब जबकि हमने addConstantएक संदर्भ में परिभाषित किया है जहां kपरिभाषित addConstantकिया गया है, एक बंद हो गया है क्योंकि इसके सभी मुक्त चर अब बंद हो गए हैं (एक मूल्य के लिए): addConstantइसे लागू किया जा सकता है और चारों ओर पारित किया जा सकता है जैसे कि यह एक फ़ंक्शन था। नोट करें कि नि: शुल्क चर kएक मूल्य के लिए बाध्य है जब बंद को परिभाषित किया गया है , जबकि तर्क चर vबाध्य है जब बंद किया जाता है

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

कई भाषाओं में, यदि आप एक बंद का उपयोग केवल एक बार करते हैं तो आप इसे गुमनाम कर सकते हैं , जैसे

def increaseAll(values: List[Int]): List[Int] =
{
  val k = 2

  values.map(v => v + k)
}

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


यह तर्क में बंद और खुले फार्मूले के साथ अच्छी तरह से लिपटता है। आपके उत्तर के लिए धन्यवाद।
रेनडॉक्टर

@ रैनडॉक्टर: फ्री वेरिएबल को लॉजिक फॉर्मूला में और लैम्ब्डा कैलकुलस एक्सप्रेशन में एक समान तरीके से परिभाषित किया गया है: लैम्ब्डा एक्सप्रेशन में लैम्ब्डा लॉजिक फॉर्मूला wrt फ्री / बाउंड वेरिएबल्स में क्वांटिफायर की तरह काम करता है।
जियोर्जियो

9

जावास्क्रिप्ट में एक सरल विवरण:

var closure_example = function() {
    var closure = 0;
    // after first iteration the value will not be erased from the memory
    // because it is bound with the returned alertValue function.
    return {
        alertValue : function() {
            closure++;
            alert(closure);
        }
    };
};
closure_example();

alert(closure)पहले निर्मित मान का उपयोग करेगा closure। लौटाया गया alertValueफ़ंक्शन नामस्थान उस नामस्थान से कनेक्ट किया जाएगा जिसमें closureचर रहता है। जब आप पूरे फ़ंक्शन को हटाते हैं, तो closureचर का मूल्य हटा दिया जाएगा, लेकिन तब तक, alertValueफ़ंक्शन हमेशा चर के मूल्य को पढ़ने / लिखने में सक्षम होगा closure

यदि आप इस कोड को चलाते हैं, तो पहला पुनरावृत्ति closureवैरिएबल को 0 मान देगा और फ़ंक्शन को फिर से लिखेगा:

var closure_example = function(){
    alertValue : function(){
        closure++;
        alert(closure);
    }       
}

और क्योंकि फ़ंक्शन को निष्पादित alertValueकरने के लिए स्थानीय चर की आवश्यकता closureहोती है, यह पहले से निर्दिष्ट स्थानीय चर के मूल्य के साथ खुद को बांधता है closure

और अब हर बार जब आप closure_exampleफ़ंक्शन को कॉल करते हैं, तो यह closureवेरिएबल के बढ़े हुए मूल्य को लिख देगा क्योंकि alert(closure)यह बाध्य है।

closure_example.alertValue()//alerts value 1 
closure_example.alertValue()//alerts value 2 
closure_example.alertValue()//alerts value 3
//etc. 

धन्यवाद, मैंने कोड का परीक्षण नहीं किया =) अब सब कुछ ठीक लग रहा है।
Muha

5

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

यह उन चीजों में से एक है, जो दुर्भाग्य से, अपरिचित होने के कारण समझाने में थोड़ा कठिन है।

अतीत में सफलतापूर्वक इस्तेमाल की गई एक सादृश्य था "कल्पना कीजिए कि हमारे पास कुछ है जिसे हम 'पुस्तक' कहते हैं, कक्ष-बंदी में, 'पुस्तक' वह है जो वहां कॉपी है, टीएओसीपी के कोने में, लेकिन टेबल-क्लोजर पर , यह एक ड्रेसडेन फाइल्स बुक की कॉपी है। इसलिए आप किस क्लोजर में हैं, इस पर निर्भर करते हुए कोड 'मुझे अलग-अलग चीजों में होने वाले परिणाम' देता है। "


आप इसे भूल गए: en.wikipedia.org/wiki/Closure_(computer_programming) अपने उत्तर में।
S.Lott

3
नहीं, मैंने समझदारी से उस पृष्ठ को बंद नहीं करने का विकल्प चुना।
वेटिन

"राज्य और कार्य।": क्या staticस्थानीय चर वाले C फ़ंक्शन को बंद माना जा सकता है? क्या हास्केल में बंद होने की स्थिति है?
जियोर्जियो

2
Haskell में @Giorgio क्लोज़र (मेरा मानना ​​है) वे जिस लेक्सिकल स्कोप में परिभाषित किए गए हैं, उसके करीब हैं, इसलिए, मैं "हाँ" कहूँगा (हालाँकि मैं हस्केल से सबसे ज्यादा अपरिचित हूँ)। एक स्थिर चर के साथ एसी फ़ंक्शन सबसे अच्छा है, एक बहुत ही सीमित बंद (आप वास्तव में एक एकल फ़ंक्शन से कई क्लोजर बनाने में सक्षम होना चाहते हैं, एक staticस्थानीय चर के साथ, आपके पास बिल्कुल एक है)।
वेटिन

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

5

यह परिभाषित करना कठिन है कि 'राज्य' की अवधारणा को परिभाषित किए बिना क्या बंद हो सकता है।

मूल रूप से, पूर्ण लेक्सिकल स्कूपिंग वाली भाषा में जो प्रथम श्रेणी के मूल्यों के रूप में कार्य करता है, कुछ विशेष होता है। अगर मुझे कुछ ऐसा करना था:

function foo(x)
return x
end

x = foo

चर xन केवल संदर्भ देता है, function foo()बल्कि यह भी संदर्भित करता है कि राज्य fooपिछली बार जब वापस लौटा था। असली जादू तब होता है जब fooअन्य कार्य इसके दायरे में आगे परिभाषित होते हैं; यह अपने स्वयं के मिनी-पर्यावरण की तरह है (बस 'सामान्य रूप से' हम वैश्विक वातावरण में कार्यों को परिभाषित करते हैं)।

कार्यात्मक रूप से यह C ++ (C?) के 'स्थिर' कीवर्ड के समान कई समस्याओं को हल कर सकता है, जो कई फ़ंक्शन कॉल में स्थानीय चर की स्थिति को बनाए रखता है; हालाँकि यह एक फ़ंक्शन में उसी सिद्धांत (स्थिर चर) को लागू करने की तरह है, क्योंकि फ़ंक्शन प्रथम श्रेणी के मान हैं; क्लोजर पूरे फ़ंक्शन की स्थिति को सहेजे जाने के लिए समर्थन जोड़ता है (C ++ के स्थैतिक कार्यों के साथ कुछ नहीं करना है)।

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

यहाँ लुआ के बंद समर्थन का कुछ परीक्षण किया गया है।

--Closure testing
--By Trae Barlow
--

function myclosure()
    print(pvalue)--nil
    local pvalue = pvalue or 10
    return function()
        pvalue = pvalue + 10 --20, 31, 42, 53(53 never printed)
        print(pvalue)
        pvalue = pvalue + 1 --21, 32, 43(pvalue state saved through multiple calls)
        return pvalue
    end
end

x = myclosure() --x now references anonymous function inside myclosure()

x()--nil, 20
x() --21, 31
x() --32, 42
    --43, 53 -- if we iterated x() again

परिणाम:

nil
20
31
42

यह मुश्किल हो सकता है, और यह संभवतः भाषा से भाषा में भिन्न होता है, लेकिन लू में ऐसा लगता है कि जब भी किसी फ़ंक्शन को निष्पादित किया जाता है, तो इसकी स्थिति रीसेट हो जाती है। मैं यह कहता हूं क्योंकि ऊपर दिए गए कोड से परिणाम अलग-अलग होंगे यदि हम myclosureसीधे फ़ंक्शन / स्थिति तक पहुंच रहे थे (इसके बजाय अनाम फ़ंक्शन के माध्यम से यह वापस लौटता है), जैसा pvalueकि 10 पर वापस रीसेट किया जाएगा; लेकिन अगर हम एक्स (अनाम फ़ंक्शन) के माध्यम से माइक्लोजर की स्थिति तक पहुंचते हैं, तो आप देख सकते हैं कि pvalueजीवित है और स्मृति में कहीं अच्छी तरह से है। मुझे संदेह है कि इसके लिए थोड़ा और अधिक है, शायद कोई कार्यान्वयन की प्रकृति को बेहतर ढंग से समझा सकता है।

पुनश्च: मैं C ++ 11 (पिछले संस्करणों में क्या है के अलावा) की एक चाट पता नहीं है तो ध्यान दें कि यह C ++ 11 और Lua में बंद होने के बीच तुलना नहीं है। इसके अलावा, Lua से C ++ तक खींची गई सभी 'लाइनें' समानताएं हैं क्योंकि स्थिर चर और क्लोजर 100% समान नहीं हैं; भले ही वे कभी-कभी समान समस्याओं को हल करने के लिए उपयोग किए जाते हैं।

जो चीज़ मुझे निश्चित नहीं है, वह ऊपर दिए गए कोड उदाहरण में है, क्या अनाम फ़ंक्शन या उच्च क्रम फ़ंक्शन को बंद माना जाता है?


4

एक क्लोजर एक फ़ंक्शन है जिसमें संबंधित स्थिति होती है:

पर्ल में आप इस तरह से क्लोजर बनाते हैं:

#!/usr/bin/perl

# This function creates a closure.
sub getHelloPrint
{
    # Bind state for the function we are returning.
    my ($first) = @_;a

    # The function returned will have access to the variable $first
    return sub { my ($second) = @_; print  "$first $second\n"; };
}

my $hw = getHelloPrint("Hello");
my $gw = getHelloPrint("Goodby");

&$hw("World"); // Print Hello World
&$gw("World"); // PRint Goodby World

यदि हम C ++ के साथ प्रदान की गई नई कार्यक्षमता को देखते हैं।
यह आपको वर्तमान स्थिति को ऑब्जेक्ट में बाँधने की अनुमति देता है:

#include <string>
#include <iostream>
#include <functional>


std::function<void(std::string const&)> getLambda(std::string const& first)
{
    // Here we bind `first` to the function
    // The second parameter will be passed when we call the function
    return [first](std::string const& second) -> void
    {   std::cout << first << " " << second << "\n";
    };
}

int main(int argc, char* argv[])
{
    auto hw = getLambda("Hello");
    auto gw = getLambda("GoodBye");

    hw("World");
    gw("World");
}

2

आइए एक साधारण कार्य पर विचार करें:

function f1(x) {
    // ... something
}

इस फ़ंक्शन को एक शीर्ष-स्तरीय फ़ंक्शन कहा जाता है क्योंकि यह किसी अन्य फ़ंक्शन के भीतर नेस्टेड नहीं है। प्रत्येक जावास्क्रिप्ट फ़ंक्शन अपने आप में वस्तुओं की एक सूची को "स्कोप चेन" कहा जाता है । यह स्कोप चेन ऑब्जेक्ट्स की एक ऑर्डर की गई सूची है। इनमें से प्रत्येक वस्तु कुछ चर को परिभाषित करती है।

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

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

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

तो, हम अब यह जानते हैं:

  • हर फंक्शन में एक स्कोप चेन जुड़ी होती है
  • जब फ़ंक्शन परिभाषित होता है (जब फ़ंक्शन ऑब्जेक्ट बनाया जाता है), तो जावास्क्रिप्ट उस फ़ंक्शन के साथ एक गुंजाइश श्रृंखला बचाता है
  • शीर्ष स्तर के कार्यों के लिए, स्कोप चेन में फंक्शन डेफिनिशन समय में केवल वैश्विक ऑब्जेक्ट होता है और आह्वान समय पर शीर्ष पर एक अतिरिक्त सक्रियण ऑब्जेक्ट जोड़ता है

स्थिति तब दिलचस्प हो जाती है जब हम नेस्टेड कार्यों से निपटते हैं। तो, चलो एक बनाएँ:

function f1(x) {

    function f2(y) {
        // ... something
    }

}

जब f1हम परिभाषित होते हैं तो हमें इसके लिए एक गुंजाइश श्रृंखला मिलती है जिसमें केवल वैश्विक वस्तु होती है।

अब जब f1कॉल किया जाता है, f1तो सक्रियण ऑब्जेक्ट को प्राप्त करने की गुंजाइश श्रृंखला होती है। इस सक्रियण ऑब्जेक्ट में चर xऔर चर है f2जो एक फ़ंक्शन है। और, ध्यान दें कि f2परिभाषित हो रहा है। इसलिए, इस बिंदु पर, जावास्क्रिप्ट भी एक नई गुंजाइश श्रृंखला बचाता है f2इस आंतरिक फ़ंक्शन के लिए सहेजी गई कार्यक्षेत्र श्रृंखला प्रभाव में वर्तमान गुंजाइश श्रृंखला है। प्रभाव में मौजूदा गुंजाइश श्रृंखला है f1। इसलिए f2की गुंजाइश श्रृंखला है f1s ' वर्तमान जो की सक्रियता वस्तु शामिल हैं - गुंजाइश श्रृंखला f1और वैश्विक वस्तु।

जब f2इसे कहा जाता है, तो यह अपनी सक्रियता वाली वस्तु हो जाती है y, इसकी स्कोप श्रृंखला में जोड़ दी जाती है जिसमें पहले से ही सक्रिय वस्तु f1और वैश्विक वस्तु शामिल होती है।

यदि कोई अन्य नेस्टेड फ़ंक्शन के भीतर परिभाषित किया गया था f2, तो इसकी गुंजाइश श्रृंखला में परिभाषा समय पर तीन ऑब्जेक्ट होंगे (दो बाहरी कार्यों के सक्रियण ऑब्जेक्ट, और वैश्विक ऑब्जेक्ट), और 4 आह्वान के समय।

तो, अब हम समझते हैं कि स्कोप चेन कैसे काम करती है लेकिन हमने अभी तक क्लोजर के बारे में बात नहीं की है।

फ़ंक्शन ऑब्जेक्ट और एक स्कोप (वैरिएबल बाइंडिंग का एक सेट) का संयोजन जिसमें फ़ंक्शन के वेरिएबल्स को हल किया जाता है, कंप्यूटर साइंस साहित्य में एक क्लोजर कहा जाता है - डेविड फ्लैनगन द्वारा निश्चित गाइड जावास्क्रिप्ट

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

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

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

हमारे उपरोक्त उदाहरण में, हम वहां f2से नहीं लौटते हैं f1, इसलिए, जब कोई कॉल f1लौटाता है, तो उसके सक्रियण ऑब्जेक्ट को उसके स्कोप चेन और एकत्रित कचरा से हटा दिया जाएगा। लेकिन अगर हमारे पास ऐसा कुछ था:

function f1(x) {

    function f2(y) {
        // ... something
    }

    return f2;
}

यहां, लौटने f2पर एक स्कोप चेन होगी जिसमें सक्रियण ऑब्जेक्ट होगा f1, और इसलिए यह कचरा एकत्र नहीं किया जाएगा। इस बिंदु पर, यदि हम कॉल करते हैं f2, तो यह f1's' वेरिएबल xतक पहुँचने में सक्षम होगा, जबकि हम बाहर हैं f1

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

कई शक्तिशाली प्रोग्रामिंग तकनीकें शामिल हैं जिनमें निजी चर, घटना संचालित प्रोग्रामिंग, आंशिक अनुप्रयोग आदि जैसे बंद शामिल हैं ।

यह भी ध्यान दें कि यह सब उन सभी भाषाओं पर लागू होता है जो क्लोजर का समर्थन करती हैं। उदाहरण के लिए PHP (5.3+), पायथन, रूबी, आदि।


-1

एक क्लोजर एक कंपाइलर ऑप्टिमाइज़ेशन (उर्फ सिंथैटिक शुगर?) है। कुछ लोगों ने इसे गरीब आदमी की वस्तु भी कहा है।

एरिक लिपर्ट द्वारा उत्तर देखें : (नीचे अंश)

संकलक इस तरह कोड उत्पन्न करेगा:

private class Locals
{
  public int count;
  public void Anonymous()
  {
    this.count++;
  }
}

public Action Counter()
{
  Locals locals = new Locals();
  locals.count = 0;
  Action counter = new Action(locals.Anonymous);
  return counter;
}

सही बात?
साथ ही, आपने तुलना करने के लिए कहा। VB और JScript दोनों एक ही तरह से क्लोजर बनाते हैं।


यह जवाब एक CW है क्योंकि मैं एरिक के महान जवाब के लिए अंक के लायक नहीं हूं। जैसा कि आप फिट देखते हैं, कृपया इसे बढ़ाएँ। HTH
goodguys_activate

3
-1: आपका स्पष्टीकरण C # में बहुत मूल है। क्लोजर का उपयोग कई भाषाओं में किया जाता है और इन भाषाओं में सिंटैक्टिक शुगर की तुलना में बहुत अधिक है और फ़ंक्शन और राज्य दोनों को शामिल करता है।
मार्टिन यॉर्क

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