2012-11-20 33 views
10

कार्यात्मक प्रोग्रामिंग (गतिशील प्रकार प्रणाली के साथ) में polymorphism का उपयोग कैसे करें?कार्यात्मक प्रोग्रामिंग में बहुरूपता का उपयोग कैसे करें?

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

ओओपी में इसे छोटे से किया जा सकता है, लेकिन एफपी में इसे कैसे किया जाए? विशेष रूप से गतिशील प्रकार प्रणाली वाली भाषाओं में, जैसे योजना, क्लोजर (स्थिर प्रकार संकलन समय पर संकल्प के बिना)?

मैंने सरल कोड बनाया (लाइव संस्करण http://tinkerbin.com/0C3y8D9Z, 'रन' बटन दबाएं)। मैंने एफपी नमूना में स्विच किया है, लेकिन यह एक बहुत बुरा दृष्टिकोण है। इस तरह की समस्या को कैसे हल किया जा सकता है?

नमूने जावास्क्रिप्ट में हैं, लेकिन यह केवल सादगी के उद्देश्य के लिए है, गतिशील टाइपिंग सिस्टम के साथ किसी भी कार्यात्मक भाषा में समाधान देखना दिलचस्प होगा।

OOP

var print = function(message){document.write(message + "\n<br/>")} 

// Object Oriented Approach. 
var circle = { 
    draw: function(){print("drawing circle ...")} 
} 
var rectangle = { 
    draw: function(){print("drawing rectangle ...")} 
} 

var objects = [circle, rectangle] 
objects.forEach(function(o){ 
    o.draw() 
}) 

एफपी

var print = function(message){document.write(message + "\n<br/>")} 

// Functional Approach. 
var circle = {type: 'Circle'} 
var drawCircle = function(){print("drawing circle ...")} 

var rectangle = {type: 'Rectangle'} 
var drawRectangle = function(){print("drawing rectangle ...")} 

var objects = [circle, rectangle] 
objects.forEach(function(o){ 
    if(o.type == 'Circle') drawCircle(o) 
    else if(o.type == 'Rectangle') drawRectangle(o) 
    else throw new Error('unknown type!') 
}) 

उत्तर

4

OO बहुरूपता कार्यात्मक प्रोग्रामिंग का एक हिस्सा नहीं है। हालांकि कुछ कार्यात्मक भाषाओं (जैसे क्लोजर) में ओओ पॉलीमोर्फिज्म है।

बहुरूपता का एक अन्य तरह मुल्टीमेथड्स

(def circle {:type :circle 
      :radius 50}) 

(def rectangle {:type :rectangle 
       :width 5 
       :height 10}) 

(defmulti draw :type) 

(defmethod draw :circle [object] 
    (println "circle: radius = " (:radius object))) 

(defmethod draw :rectangle [object] 
    (println "rectangle: " 
      "width = " (:width object) 
      "height = " (:height object))) 

(doseq [o [rectangle circle]] (draw o)) 
=> rectangle: width = 5 height = 10 
    circle: radius = 50 

है या आप केवल

(defn circle [] (println "drawing circle ...")) 
(defn rectangle [] (println "drawing rectangle ...")) 

(def objects [circle rectangle]) 

(doseq [o objects] (o)) 
=> drawing circle ... 
    drawing rectangle ... 
+0

मुझे multimethods के बारे में आपकी बात पसंद है, लेकिन मुझे समझ में नहीं आता कि आप दूसरे कोड स्निपेट के साथ क्या प्राप्त कर रहे हैं _ या आप केवल कार्यात्मक शैली का उपयोग कर सकते हैं _... – DaoWen

+0

@DaoWen एफपी समाधान में अतिरिक्त बिना आसानी से हासिल किया जा रहा है कपोल-कल्पना। – mobyte

+1

उस स्थिति में मुझे लगता है कि आपको अपने कोड में एक त्रुटि है क्योंकि यह समझ में नहीं आता है। '(डीफ ऑब्जेक्ट्स [ड्रा-सर्कल ड्रा-आयत]]' ??? उन कार्यों को कहाँ परिभाषित किया गया है? – DaoWen

2

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

आपके साधारण उदाहरण में जहां केवल एक ही कार्य है, आप केवल objects = [drawCircle, drawRectangle] जैसे कार्यों की सूची भी बना सकते हैं।

+0

धन्यवाद, हमम, हां, लेकिन फिर मुझे कार्यात्मक प्रोग्रामिंग का उपयोग करने में बिंदु नहीं दिख रहा है, यह ओओपी के पुनर्विचार की तरह दिखता है ... –

+1

@AlexeyPetrushin कार्यात्मक प्रोग्रामिंग पहले आया, इसलिए यदि कुछ भी ओओपी एफपी का पुनर्विचार है। इसके अलावा एफओ दृष्टिकोण उन मामलों में अधिक स्पष्ट रूप से अलग होगा जहां ओओपी दृष्टिकोण म्यूटेबल राज्य पर निर्भर करता है। – sepp2k

2

मुख्य रूप से कार्यात्मक प्रोग्रामिंग के लिए डिज़ाइन की गई कई भाषाओं में, पॉलिमॉर्फिज्म को प्राप्त करने के तरीके (विज्ञापन-प्रसार, जैसा कि इसे कहा जाता है) प्राप्त करने के तरीके हैं, हालांकि वे पॉलिमॉर्फिज्म को कॉल करते हैं।

class Draw a where 
    draw :: a -> SomethingSomthing -- probably IO() for your example, btw 

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

data Circle = Circle Point Double -- center, radius 
data Rectangle = Rect Point Double Double -- center, height, width 

instance Draw Circle where 
    draw (Circle center radius) = … 
instance Draw Rectangle where 
    draw (Rect center height width) = … 

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

एक और तरीका यह है कि आपका जेएस स्निपेट क्या करता है (जिस तरह से, आप पॉलिमॉर्फिज्म प्राप्त करने के लिए क्या करेंगे, यदि आपके पास प्रत्येक प्रकार की कोई वस्तु नहीं है, और इस संस्करण में एक ही समस्या है): एक फ़ंक्शन एम्बेड करें जो प्रत्येक ऑब्जेक्ट में पॉलीमोर्फिक व्यवहार करता है। एक मायने में, आपका "ओओपी" स्निपेट पहले ही कार्यात्मक है।

data Drawable = Drawable (Drawable -> SomethingSomething) {- other fields -} 
draw (Drawable draw) = draw Drawable 

हालांकि एक स्थिर भाषा में, यह अलग-अलग वस्तुओं के अलग-अलग गुणों की अनुमति नहीं देता है।

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

data Shape = Circle {- see second snippet -} 
      | Rect {- ditto -} 

draw (Circle center radius) = … 
draw (Rect center height width) = … 
+1

धन्यवाद, लेकिन इन सभी नमूनों - हास्केल, स्काला और प्रकार पैटर्न मिलान को स्थिर रूप से टाइप किया गया है और संकलन समय पर कार्यों को हल किया गया है। –

+0

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

+0

@AlexeyPetrushin प्रकार वर्ग "विधियों" (मेरे उदाहरण में, 'ड्रा :: ड्रा = = ए -> कुछ कुछ') देर से बाध्य हैं, और इस प्रकार संकलन समय के बजाय रनटाइम पर भेजा जाता है। आपके पास अस्तित्व के प्रकारों के साथ विभिन्न प्रकार के आकारों की एक सूची हो सकती है, प्रत्येक पर 'ड्रा' पर कॉल करें, और प्रत्येक के लिए सही कार्यान्वयन प्राप्त करें। – delnan

11

आपका "एफपी" संस्करण नहीं है मैं मुहावरेदार एफपी उदाहरण क्या करने पर विचार कार्यात्मक शैली का उपयोग कर सकते हैं। एफपी में, आप अक्सर वेरिएंट और पैटर्न मिलान का उपयोग करते हैं जहां ओओपी में आप कक्षाओं और विधि प्रेषण का उपयोग करेंगे।

var circle = {type: 'Circle'} 
var rectangle = {type: 'Rectangle'} 

var draw = function(shape) { 
    switch (shape.type) { 
    case 'Circle': print("drawing circle ..."); break 
    case 'Rectangle': print("drawing rectangle ..."); break 
    } 
} 

var objects = [circle, rectangle] 
objects.forEach(draw) 

(बेशक, कि जावास्क्रिप्ट है एक कार्यात्मक भाषा में आप आम तौर पर इस बात के लिए भी बहुत कुछ सुरुचिपूर्ण और संक्षिप्त वाक्य रचना, जैसे है::।

विशेष रूप से, आप केवल एक draw समारोह है कि पहले से ही प्रेषण आंतरिक रूप से करता है
draw `Circle = print "drawing circle..." 
draw `Rectangle = print "drawing rectangle..." 

objects = [`Circle, `Rectangle] 
foreach draw objects 

)

अब, औसत OO प्रशंसक उपरोक्त कोड देख सकते हैं और करेंगे कहते हैं: "लेकिन OO समाधान एक्स्टेंसिबल है, ऊपर नहीं है!" यह इस अर्थ में सच है कि आप आसानी से ओओ संस्करण में नए आकार जोड़ सकते हैं और जब आप ऐसा करते हैं तो मौजूदा किसी भी मौजूदा (या उनके draw फ़ंक्शन) को स्पर्श करने की आवश्यकता नहीं है। एफपी मार्ग के साथ, आपको draw फ़ंक्शन, और अन्य सभी परिचालनों का विस्तार करना होगा।

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

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

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

कुछ आगे की टिप्पणी:

  • कार्यक्रम संगठन में यह अलग अलग वरीयता एफपी के केंद्रीय विचार नहीं है।अधिक मायने रखता है कि उत्परिवर्तनीय राज्य को हतोत्साहित किया जा रहा है, और अत्यधिक पुन: प्रयोज्य उच्च-आदेश abstractions को प्रोत्साहित किया जाता है।

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

  • आपके प्रश्न के प्रकारों के साथ बहुत कम करना है। बेवकूफ ओओ और बेवकूफ एफपी समाधान दोनों एक untyped या टाइप की गई भाषा में काम करते हैं।

4

Clojure में प्रोटोकॉल जो हास्केल के प्रकार वर्ग के रूप में मूलतः एक ही तदर्थ बहुरूपता प्रदान कर रहे हैं:

(extend-protocol shape 
    String (draw [_] "I am not a shape, but who cares?")) 

और फिर:

(defprotocol shape (draw [e])) 
(defrecord circle [radius]) 
(defrecord rectangle [w h]) 
(extend-protocol shape 
    circle (draw [_] "I am a nice circle") 
    rectangle (draw [_] "Can I haz cornerz please?")) 

तुम भी एक मौजूदा प्रकार विस्तार कर सकते हैं आप कुछ उदाहरणों के लिए ड्रॉ विधि लागू कर सकते हैं

user=> (map draw [(->circle 1) (->rectangle 4 2) "foo"]) 
("I am a nice circle" "Can I haz cornerz please?" "I am not a shape, but who cares?") 
+1

क्लोजर को छोड़कर इन्हें सूची में रखने के लिए अस्तित्वत्मक मात्रा की आवश्यकता नहीं है। – Cubic