क्लोजर प्रोटोकॉल की सरल व्याख्या


131

मैं क्लोजर प्रोटोकॉल को समझने की कोशिश कर रहा हूं और उन्हें किस समस्या को हल करना चाहिए। क्या किसी के पास क्लोजर प्रोटोकॉल के whats और whys की स्पष्ट व्याख्या है?


7
क्लोजर 1.2 प्रोटोकॉल 27 मिनट में: vimeo.com/11236603
मीकू

3
प्रोटोकॉल के बहुत करीब सादृश्य
स्कैल

जवाबों:


284

क्लोजर में प्रोटोकॉल का उद्देश्य अभिव्यक्ति की समस्या को एक कुशल तरीके से हल करना है।

तो, अभिव्यक्ति समस्या क्या है? यह एक्स्टेंसिबिलिटी की मूल समस्या को संदर्भित करता है: हमारे प्रोग्राम ऑपरेशन का उपयोग करके डेटा प्रकारों में हेरफेर करते हैं। जैसा कि हमारे कार्यक्रम विकसित होते हैं, हमें उन्हें नए डेटा प्रकार और नए संचालन के साथ विस्तारित करने की आवश्यकता होती है। और विशेष रूप से, हम नए ऑपरेशन को जोड़ने में सक्षम होना चाहते हैं जो मौजूदा डेटा प्रकारों के साथ काम करते हैं, और हम नए डेटा प्रकारों को जोड़ना चाहते हैं जो मौजूदा संचालन के साथ काम करते हैं। और हम चाहते हैं कि यह सही विस्तार हो , अर्थात हम मौजूदा को संशोधित नहीं करना चाहते हैंकार्यक्रम, हम मौजूदा सार का सम्मान करना चाहते हैं, हम चाहते हैं कि हमारे एक्सटेंशन अलग-अलग मॉड्यूल हों, अलग-अलग नामस्थानों में, अलग-अलग संकलित, अलग से तैनात किए गए, अलग-अलग प्रकार के चेक किए गए। हम चाहते हैं कि वे टाइप-सेफ रहें। [नोट: इन सभी का सभी भाषाओं में अर्थ नहीं है। लेकिन, उदाहरण के लिए, उन्हें टाइप-सेफ रखने का लक्ष्य क्लोजर जैसी भाषा में भी समझ में आता है। सिर्फ इसलिए कि हम सांख्यिकीय रूप से टाइप-सेफ्टी की जांच नहीं कर सकते हैं, इसका मतलब यह नहीं है कि हम अपना कोड बेतरतीब ढंग से तोड़ना चाहते हैं?]

अभिव्यक्ति की समस्या है, आप वास्तव में किसी भाषा में इतनी व्यापकता कैसे प्रदान करते हैं?

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

func print(node):
  case node of:
    AddOperator => print(node.left) + '+' + print(node.right)
    NotOperator => '!' + print(node)

func eval(node):
  case node of:
    AddOperator => eval(node.left) + eval(node.right)
    NotOperator => !eval(node)

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

और विशिष्ट भोले OO के लिए, आपको सटीक विपरीत समस्या है: नए डेटा प्रकारों को जोड़ना आसान है जो मौजूदा संचालन के साथ काम करते हैं (या तो इनहेरिट करके या उन्हें ओवरराइड करके), लेकिन नए संचालन को जोड़ना मुश्किल है, क्योंकि मूल रूप से संशोधित करना मौजूदा कक्षाएं / वस्तुएं।

class AddOperator(left: Node, right: Node) < Node:
  meth print:
    left.print + '+' + right.print

  meth eval
    left.eval + right.eval

class NotOperator(expr: Node) < Node:
  meth print:
    '!' + expr.print

  meth eval
    !expr.eval

यहां, एक नया नोड प्रकार जोड़ना आसान है, क्योंकि आप या तो सभी आवश्यक कार्यों को इनहेरिट करते हैं, ओवरराइड करते हैं या कार्यान्वित करते हैं, लेकिन एक नया ऑपरेशन जोड़ना कठिन है, क्योंकि आपको इसे या तो सभी लीफ क्लास या बेस क्लास में जोड़ने की आवश्यकता है, इस प्रकार मौजूदा संशोधन कोड।

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

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

तो, चलो मल्टीमिथोड्स के साथ प्रोटोकॉल की तुलना करते हैं, क्योंकि दोनों एक ही काम करते हैं। या, इसे दूसरे तरीके से रखने के लिए: अगर हमारे पास पहले से ही मल्टीमिथोड हैं तो प्रोटोकॉल क्यों ?

मल्टीमिथोड्स के ऊपर मुख्य बात यह है कि प्रोटोकॉल बहुस्तरीय है: आप एक साथ कई कार्य कर सकते हैं और कह सकते हैं कि "ये 3 कार्य एक साथ प्रोटोकॉल का निर्माण करते हैं Foo"। आप मल्टीमिथोड्स के साथ ऐसा नहीं कर सकते, वे हमेशा अपने दम पर खड़े रहते हैं। उदाहरण के लिए, आप यह घोषणा कर सकते हैं कि Stackप्रोटोकॉल में एक और एक दोनों फ़ंक्शन शामिल हैंpushpop

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

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

तो, प्रोटोकॉल आप प्रेषण करने के लिए प्रतिबंधित केवल पर पहले तर्क और केवल अपने प्रकार पर (या किसी विशेष मामले के रूप nil)।

प्रति प्रोटोकॉल प्रोटोकॉल के विचार पर यह एक सीमा नहीं है, यह अंतर्निहित मंच के प्रदर्शन अनुकूलन के लिए उपयोग करने के लिए एक व्यावहारिक विकल्प है। विशेष रूप से, इसका मतलब है कि प्रोटोकॉल में जेवीएम / सीएलआई इंटरफेस की एक मामूली मैपिंग है, जो उन्हें बहुत तेज बनाता है। काफी तेजी से, वास्तव में, क्लोजर के उन हिस्सों को फिर से लिखने में सक्षम होने के लिए जो वर्तमान में जावा या सी # क्लोअर में ही लिखे गए हैं।

क्लोजर वास्तव में पहले से ही संस्करण 1.0 से प्रोटोकॉल है: Seqउदाहरण के लिए, एक प्रोटोकॉल है। लेकिन 1.2 तक, आप क्लोजर में प्रोटोकॉल नहीं लिख सकते थे, आपको उन्हें होस्ट भाषा में लिखना होगा।


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

3
अभिव्यक्ति समस्या और clojure के प्रोटोकॉल पर एक उत्कृष्ट लेख - ibm.com/developerworks/library/j-clojure-protocols
navgeet

इस तरह के एक पुराने उत्तर पर टिप्पणी पोस्ट करने के लिए क्षमा करें, लेकिन क्या आप विस्तार से बता सकते हैं कि एक्सप्रेशन समस्या का एक अच्छा समाधान एक्सटेंशन और इंटरफेस (C # / Java) क्यों नहीं हैं?
ओनोरियो कैटेनेशिआ

जावा में इस अर्थ में एक्सटेंशन नहीं है कि शब्द का उपयोग यहां किया गया है।
user100464

रूबी में शोधन है जो बंदर के पेटिंग को अप्रचलित बनाता है।
मार्सिन बिल्स्की

64

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

एक उदाहरण:

(defprotocol my-protocol 
  (foo [x]))

एक फ़ंक्शन "फू" नामक एक प्रोटोकॉल को परिभाषित करता है जो एक पैरामीटर "x" पर कार्य करता है।

आप तब डेटा संरचनाएँ बना सकते हैं जो प्रोटोकॉल को लागू करती हैं, जैसे

(defrecord constant-foo [value]  
  my-protocol
    (foo [x] value))

(def a (constant-foo. 7))

(foo a)
=> 7

ध्यान दें कि यहां प्रोटोकॉल को लागू करने वाली वस्तु को पहले पैरामीटर के रूप में पारित किया गया है x- कुछ हद तक अंतर्निहित "यह" ऑब्जेक्ट ओरिएंटेड भाषाओं में।

प्रोटोकॉल की बहुत शक्तिशाली और उपयोगी विशेषताओं में से एक यह है कि आप उन्हें ऑब्जेक्ट में विस्तारित कर सकते हैं, भले ही ऑब्जेक्ट मूल रूप से प्रोटोकॉल का समर्थन करने के लिए डिज़ाइन नहीं किया गया हो । उदाहरण के लिए यदि आप चाहें तो आप java.lang.String वर्ग के ऊपर प्रोटोकॉल का विस्तार कर सकते हैं:

(extend-protocol my-protocol
  java.lang.String
    (foo [x] (.length x)))

(foo "Hello")
=> 5

1
> निहित अर्थ की तरह "यह" पैरामीटर ऑब्जेक्ट ओरिएंटेड भाषा में मैंने देखा कि प्रोटोकॉल फ़ंक्शंस के लिए पास किया गया thisसंस्करण अक्सर क्लोजर कोड में भी कहा जाता है।
क्रिश
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.