2010-12-03 11 views
14

पर माइग्रेट करने में समस्याएं मैं कार्यात्मक प्रोग्रामिंग (मुख्य रूप से हास्केल) के साथ काम करने के लिए उपयोग किया जाता हूं और मैं ओओ (स्कैला) से शुरुआत कर रहा हूं।कार्यात्मक से ओओ

मुझे अपने कोड का अनुवाद करने में परेशानी हो रही है। उदाहरण के लिए, यह बी-पेड़ की मेरी हास्केल परिभाषा है:

data BTree a = 
    Leaf 
    |Node2 (BTree a) a (BTree a) 
    |Node3 (BTree a) a (BTree a) a (BTree a) 
    deriving (Eq,Read,Show) 

यह काफी आसान है। मेरा पेड़ खाली है, या इसका मूल्य है और यह दो पेड़ों का पिता है या यह 3 उप पेड़ का पिता है।

ओओ पर क्या है? मेरे पास कोई सुराग नहीं है। मैं बस यह नहीं समझ सकता कि मैं इसे एक तरह से कैसे कर सकता हूं।

+0

यहां वास्तविक प्रश्न क्या है? ओओ में बी-पेड़ कैसे लागू किए जाते हैं? –

+0

नहीं। ओओ में यह या रिलाटिन क्या है। यह विरासत नहीं है, न ही रचना ... क्या यह कुछ है? – Bruna

+6

प्रश्न मेरे लिए पर्याप्त स्पष्ट लगता है: एक ऑब्जेक्ट उन्मुख फैशन में उपर्युक्त विशेषताओं वाला एक पेड़ कैसे मॉडल करता है? –

उत्तर

12

चूंकि आप अपने टैग-सूची में स्काला है, यहाँ है कि यह कैसे स्काला में किया जाएगा है कक्षाएं। इस तरह आप उन्हें स्कैला पैटर्न मिलान में भी उपयोग कर सकते हैं।

sealed trait Tree[+A] 

case object Leaf extends Tree[Any] 
case class Node2[+A](a: A, t1: Tree[A], t2: Tree[A]) extends Tree[A] 
case class Node3[+A](a: A, b: A, t1: Tree[A], t2: Tree[A], t2: Tree[A]) extends Tree[A] 

जावा (1.5 के बाद से), सी ++ और सी # जैसी भाषाओं में आप प्रकार सुरक्षा में मदद करने के टेम्पलेट्स के एक ही तरह की है। वे मूल रूप से हास्केल में प्रकार चर के समान काम करते हैं।

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

+4

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

+1

यह वास्तव में नई ओओ शैली है। इसमें दो फायदे हैं: सुरक्षा टाइप करें और आप सुनिश्चित कर सकते हैं, उदाहरण के लिए, फ़ंक्शन के लिए केवल नोड 2 या नोड 3 पैरामीटर लेने के लिए। यह मेरी राय में, ऊपर की ओर लायक है। पुरानी शैली, आप इस प्रकार को किसी ऑब्जेक्ट में परिवर्तित कर देते थे और प्रत्येक कन्स्ट्रक्टर को एक वास्तविक वर्ग कन्स्ट्रक्टर में बदल देते थे, लेकिन इस तरह हर पत्ता को 3 उप-पेड़ों के लिए कमरे ले जाने की आवश्यकता होती है, भले ही आपको इसकी आवश्यकता नहीं होगी। – Lanbo

+6

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

3

परिभाषित करें "सीन"। असल में, यह अच्छा है: यह पहली बार है जब मैंने किसी को दूसरी तरफ कार्यात्मक से ओओ में जाने में परेशानी महसूस की है।

सच्चाई यह है कि आपको इसे करने के लिए और अधिक सामान होने जा रहे हैं; यह एक कारण है कि कार्यात्मक अच्छा है। आप एक विशिष्ट मामला मार रहे हैं जिसमें कार्यात्मक फायदे हैं।

एक OO भाषा में (यह, किसी भी विशिष्ट भाषा होना नहीं है सिर्फ स्यूडोकोड) आप

class Node 
    children : array of Node; 
end 

नोड

के लिए एक वर्ग की जरूरत के लिए जा रहे हैं और फिर आप तरीकों के लिए, उदाहरण के लिए, एक बच्चे के रूप में एक नोड जोड़ें ताकि आप इसके साथ चीजें कर सकें।

फिर आप नोड का उपयोग करके बीटीआर क्लास बनाते हैं ताकि सम्मिलन, संतुलन और अन्य कुछ भी हो सके।

+1

मुझे लगता है कि यह प्रश्न कुछ प्रोग्रामिंग पाठ्यक्रम से उत्पन्न होते हैं। ब्रुना ने हास्केल के बारे में पहला प्रोग्रामिंग कोर्स किया होगा, और दूसरे में वे स्कैला पर स्विच कर चुके थे। मुझे अपने विश्वविद्यालय में कुछ साल पहले समानताएं बदल रही थीं, प्रति वर्ष स्विचिंग प्रतिमान (मेरे मामले में हास्केल से जावा तक)। – mkluwe

+0

मैं सिर्फ ओओ तरीके से नहीं सोच सकता! सौ से मैं कामकाज किए बिना इसे ठीक से करने की कोशिश कर रहा हूं। क्या ओओ में इस संबंध या संबंध के समान कुछ है? लेकिन अगर इसे एक तरह से करना संभव नहीं है, तो मुझे पागल सलाह प्राप्त करना अच्छा लगेगा। – Bruna

17

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

"ठीक है, यहां मेरे डेटा की संरचना है, अब मैं ..." के बारे में सोच रहा हूं, इसके पीछे जा रहा है। इस तरह

कोशिश कुछ: पता लगाना क्या मौलिक कार्यों कि अपने बी-वृक्ष (show और fmap यहाँ तरह बातें मत भूलना) के साथ किया जा सकता है द्वारा

  • प्रारंभ और वर्ग डिजाइन उन पर आधारित है।

  • अपने पेड़ की तरह एक योग प्रकार के लिए, बेस क्लास को खाली छोड़ना और डेटा कन्स्ट्रक्टर पर विभिन्न भिन्नताओं के लिए उप-वर्गों का उपयोग करना आसान हो सकता है। ओओ में अंगूठे के नियम के रूप में, कहीं भी आपको किसी प्रकार की पसंद करना पड़ता है जो बाद के व्यवहार में भारी बदलाव करता है, मामलों को अलग करने के लिए उपप्रकार बहुरूपता का उपयोग करने पर दृढ़ता से विचार करें।

  • आंतरिक प्रतिनिधित्व के बारे में चिंता न करने की कोशिश न करें, और कक्षा के बाहर प्रतिनिधित्व विवरण को रिसाव न करें। GetFoo() विधियों का एक गुच्छा जो प्राचीन प्रकार लौटाता है, यह गलत करने का संकेत है।

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

आप एक आधार विशेषता (हास्केल प्रकार में) है, और व्युत्पन्न है कि सभी हास्केल कंस्ट्रक्टर्स से:

+2

"अमूर्त व्यवहार के अपारदर्शी ग्लोब" के लिए +1 – missingfaktor

+2

+1 "डिज़ाइन पैटर्न पुस्तक के माध्यम से फ़्लिप करें और आपको पता चलेगा कि इसका आधा बारोक भाषा, गायब भाषा सुविधाओं के लिए उच्च रखरखाव के कामकाज के बारे में है।" और क्योंकि यह लगभग क्रिसमस है! – soc

3

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

public abstract class BTree<A> { 
    public static final BTree LEAF = new BTree { 
     //concrete implementations of the methods 
     public boolean isEmpty(){ return true; } 
     public String toString() { return ""; } 
    } 
    public abstract boolean isEmpty(); 
    //concrete methods that are the same for all sub-classes 
    //abstract methods that are different 
} 

public class Node2<A> { 
    private BTree<A> left; 
    private BTree<A> right; 
    private A a; 

    public Node2(BTree<A> left, A a, BTree<A> right) { 
     this.left = left; 
     this.a = a; 
     this.right = right; 
    } 

    public String toString() { 
     return "(" + left + a + right + ")"; 
    } 

    public boolean equals(Object o) { 
     if (o instanceof Node2) { 
      Node2<A> n2 = (Node2<A>) n2; 
      return a.equals(n2.a) && left.equals(n2.left) && right.equals(n2.right); 
     } 
     return false;  
    } 

    public boolean isEmpty(){ return false; } 
    //other concrete methods 
} 

//same for Node3 
+2

उह ... क्या वह पेट एसिड था जिसे मैंने अभी स्वाद दिया था? ;) –

+0

नोड 2 को बीटीआई का विस्तार करना चाहिए, मुझे लगता है कि यहां उदाहरण के लिए – adamax

18

यहाँ कुछ अच्छी जवाब है, लेकिन मैं उन्हें लगता है कि आप जिस बिंदु को याद कर रहे हैं उसे दिखाने का अवसर याद करते हैं। इसलिए, आपने यह दिखाया है:

data BTree a = 
    Leaf 
    |Node2 (BTree a) a (BTree a) 
    |Node3 (BTree a) a (BTree a) a (BTree a) 
    deriving (Eq,Read,Show) 

और पूछा कि आप इसे ऑब्जेक्ट उन्मुख फैशन में कैसे कार्यान्वित करते हैं। तो, यहाँ यह है:

सबसे महत्वपूर्ण बात

trait Tree[A] { 
    // not required because it is inherited from AnyRef 
    // def equals(other: Any): Boolean 

    // not required because it is inherited from AnyRef 
    // def toString: String 

    // does not belong in the object 
    // def fromString(input: String): Tree[A] 

    // Stuff that is missing but is needed 
    def isEmpty: Boolean 
    def value: Option[A] 
    def subtrees: Seq[Tree[A]] 
    def iterator: Iterator[A] 
    def depthFirstIterator: Iterator[A] 
    def breadthFirstIterator: Iterator[A] 
} 

तो, यहाँ सौदा है: जब आप वस्तु उन्मुखीकरण के बात करते हैं, आप एक BTREE, एक उंगली पेड़, या कोई अन्य वृक्ष संरचना है अप्रासंगिक है। वास्तव में, यह छिपा होना चाहिए। प्रासंगिक क्या है आप कर सकते हैं।

आपको ऐसा करने में परेशानी हो रही है क्योंकि आप उस दिशा से समस्या का सामना कर रहे हैं जो आपको नहीं करना चाहिए।

नहीं-तो-महत्वपूर्ण बात यह है

sealed abstract class BTree[A] extends Tree[A] 
object BTree { 
    def apply[A](input: String): BTree[A] = { /* factory */ null.asInstanceOf[BTree[A]] } 
    private case object Leaf extends BTree[Nothing] { 
    // method implementation 
    } 
    private case class Node2[A](value: A, private one: BTree[A], private two: BTree[A]) extends BTree[A] { 
    // method implementation 
    } 
    private case class Node3[A](value: A, private one: BTree[A], private two: BTree[A], private three: BTree[A]) extends BTree[A] { 
    // method implementation 
    } 
} 

अब यहाँ आप वास्तव में एक कार्यान्वयन प्रस्तुत करते हैं, लेकिन BTREE के विवरण पूरी तरह से छिपा रहे हैं। आप केवल उन विधियों का उपयोग करते हैं जिन्हें Tree परिभाषित किया गया है।

यह आदर्श वस्तु उन्मुख वास्तुकला है: ग्राहक इंटरफेस पर निर्भर करते हैं, डेटा संरचना छिपी हुई है।

+0

+1 है। यह भी स्पष्ट करता है कि मेरा मतलब "संरचना-प्रथम" दृष्टिकोण से पीछे क्या है। मुझे पता है कि मैं यह कह रहा हूं, लेकिन मुझे वास्तव में स्कैला सीखना है ... –

1

मुझे स्कैला नहीं पता, लेकिन मुझे जावा पता है।

जावा में, और ऑब्जेक्ट, अक्सर एक विशिष्ट चीज़ मॉडल करेगा, उदाहरण: एक कार, या बी-पेड़ इत्यादि।

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

अब किसी भी समय किसी ऑब्जेक्ट में सटीक राज्य होगा।

तो अगर हम एक बाइनरी पेड़ वस्तु के बारे में सोचते हैं। हम एक द्विआधारी पेड़ (पूर्णांकों युक्त) कि वास्तव में इस तरह दिखता है हो सकता है:

 4 
    / \ 
    2  1 
/ /\ 
1  3 1 

तो किसी भी बिंदु समय में यह निश्चित मूल्यों के साथ नोड्स, कुछ मायनों में संलग्न की एक निश्चित संख्या के लिए जा रहा है पर।

अब हमें यह तय करने की आवश्यकता है कि हमारे बाइनरी पेड़ के बारे में जानकारी कैसे संग्रहीत करें। यह प्रत्येक बाइनरी पेड़ वस्तु में आंतरिक रूप से किया जाएगा।

तो हम जानते हैं कि प्रत्येक बाइनरी पेड़ को कुछ नोड्स बनाने जा रहे हैं।

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

तो प्रत्येक नोड ऑब्जेक्ट को मूल्य के लिए चर होना आवश्यक होगा, एक चर जो हमें बताता है कि उसका बायां बच्चा क्या है (यदि कोई है), और उसके लिए सही बच्चा है।

तो एक नोड युक्त पूर्णांक मूल्यों के लिए हम जा सकते हैं:

class Node { 
    int value; 
    Node left; 
    Node right; 

} 

अब नहीं सभी नोड्स बाईं या दाईं बच्चे (या किसी भी बच्चों को सभी) होगा। बाएं बच्चे की कमी को बाएं चर द्वारा दर्शाया जाता है जिसमें वास्तविक नोड का संदर्भ देने के बजाय मूल्य 'शून्य' होता है।

उपर्युक्त कोड सामान्य स्थिति में बिना किसी नोड का प्रतिनिधित्व करता है, लेकिन हमारे द्वारा बनाए गए एक विशेष नोड के पास 'मान', 'बाएं' और 'दाएं' चर के लिए एक विशेष मान होगा।

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

class BinaryTree { 
    Node root; 


} 

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

सबसे पहले हमें "कन्स्ट्रक्टर" की आवश्यकता है ताकि हम एक बाइनरी पेड़ ऑब्जेक्ट बना सकें, और इसे कुछ प्रारंभिक मानों पर सेट कर सकें। तो हम सिर्फ हमारे बाइनरी पेड़ खाली होने शुरू कर देंगे। हम 'रूट' परिवर्तनीय शून्य होने के द्वारा इसका प्रतिनिधित्व करते हैं। इसका मतलब है कि हमारे पास जड़ भी नहीं है! तो यह खाली है।हम एक विधि (एक समारोह जो एक वर्ग/वस्तु के अंतर्गत आता है) को छोड़कर हम इसे वर्ग को उसी नाम देने के रूप में एक ही रूप में एक निर्माता लिखें:

class BinaryTree { 
    Node root; 

    BinaryTree() { 
     root = null; // make it so that newly made objects start off being empty 
    } 
} 

हम शायद हमारे द्विआधारी देने के लिए चाहता हूँ वृक्ष कुछ व्यवहार/विधियों को ऑब्जेक्ट करता है ताकि हम वास्तव में जो भी बाइनरी पेड़ चाहते हैं उसका निर्माण कर सकें। केवल खाली पेड़ बनाने में सक्षम होने और उन्हें बदलने में शायद उपयोगी नहीं होगा!

तो हम एक addLeftChild (नोड addFrom, int मान), और addRightChild (नोड addFrom, int मान), विधियों बना सकते हैं। AddLeftChild दिए गए मान (और कोई बच्चा नहीं) वाला एक नया नोड बना देगा, और इसे एडफ्रॉम द्वारा दिए गए नोड का बायां बच्चा बना देगा, लेकिन केवल अगर 'एडफ्रॉम' नोड में पहले से ही बाएं बच्चे नहीं हैं।

class BinaryTree { 
    Node root; 

    BinaryTree() { 
     root = null; // make it so that newly made objects start off being empty 
    } 

    Node addLeftChild(Node addFrom, int value) { 
      // change the binary tree's state somehow to achieve this 
    } 

    Node addRightChild(Node addFrom, int value) { 
      // change the binary tree's state somehow to achieve this 
    } 

} 

हम यह भी एक नया रूट नोड, addRoot (पूर्णांक मान) को जोड़ने के लिए एक तरीका है हो सकता है, इसलिए जब हम पहली बार एक द्विआधारी पेड़ बना हम एक रूट नोड जोड़ सकते हैं। नोड्स को हटाने के लिए आपके पास शायद विधियों (व्यवहार) भी होंगे। आपके पास मूल्य/नोड्स के लिए पेड़ को खोजने के लिए या पेड़ के बारे में जानकारी देने के तरीके हो सकते हैं (उदाहरण: गहराई, नोड्स की संख्या)।

तब हम वास्तव में द्विआधारी पेड़ वस्तुओं बनाने के लिए कुछ कोड लिख सकते हैं, किसी तरह से उनके साथ कैसा व्यवहार, जैसे:

// this is some main method,etc 
BinaryTree ourBT = new BinaryTree(); // make an new binary tree 
            // remember these start off empty 

Node rootNode; // variable so we can tell 
       // later add methods which node to add from 

rootNode = ourBT.addRoot(4); 

यह हमें इस अब तक देना होगा इस द्विआधारी पेड़ ourBT कहा जाता है (सिर्फ एक रूट के रूप में नोड)

  4 

तो हम जा सकते हैं:

ourBT.addLeftChild(rootNode, 3); // remember the parameter rootNode refers 
           // to the root node we just added before 

जो हमारे द्विआधारी tre छोड़ना होगा इस राज्य में ई:

  4 
     /
     3 

तो हम जा सकते हैं:

  4 
     /\ 
     3 1 

तो हमारे द्विआधारी पेड़ हम कर सकते थे के निर्माण के बाद:

ourBT.addRightChild(rootNode, 1); 

जो हमारे द्विआधारी पेड़ इस राज्य में छोड़ना होगा हो सकता है कि इसके साथ कुछ और दिलचस्प चीजें करें (उदाहरण: खोज, हटाने)

यह शायद सबसे अच्छा एक्सा नहीं है mple, लेकिन उम्मीद है कि मैंने आपको थोड़ा सा अंतर्दृष्टि दी है कि ओओ शैली में कस्टम डेटा संरचनाएं कैसे लिखी जाती हैं।

संबंधित मुद्दे