2009-02-11 10 views
5

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

trait GNode[Graph] 
{ 
... functions to get edges from this vertex, etc. ... 
} 

trait Graph 
{ 
    type Node <: GNode[Graph] 
} 

def dfs[G <: Graph](g : G, nodeAction : G#Node => Unit) = ... code ... 

कोशिश की, लेकिन यह काम नहीं किया, क्योंकि जब मैंने

class ConcreteGraph extends Graph 
{ 
    class Node extends GNode[ConcreteGraph] { ... } 
} 

DFS समारोह किया nodeAction के रूप में प्रकार ConcreteGraph#Node=>Unit के एक समारोह को स्वीकार नहीं करेंगे, लेकिन केवल AnyRef=>Unit या GNode[ConcreteGraph]=>Unit

साफ होने के लिए, अगर मैं सी ++ में यह किया है, मैं एक विस्तृत ग्राफ संरचना के

template <class T> struct graph_traits; 
template <> struct graph_traits<concrete_graph> 
{ typedef concrete_graph::node node_type; } 

template <class G> 
void dfs(const G& g, boost::function<void(
      const graph_traits<G>::node_type&)> action) { ... } 

उत्तर

7

एक बहुत अच्छा उदाहरण की तरह कुछ करना चाहते हैं http://www.scala-lang.org/node/124

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

एक बार हो जाने पर, डीएफएस लिखने का पहला तरीका यह एक विधि बनाना है (यदि आप आभासी प्रेषण ओवरहेड से बचना चाहते हैं तो यह अंतिम हो सकता है)।

trait GNode[+Graph] { 
//... functions to get edges from this vertex, etc. ... 
} 

trait Graph { 
    type Node <: GNode[Graph] 

    def dfs(nodeAction : Node => Unit) = print("dfsing!") 
} 

class ConcreteGraph extends Graph { 
    class CGNode extends GNode[ConcreteGraph] 
    type Node <: CGNode 
} 

new ConcreteGraph dfs {node => println("foo")} 

दूसरा, डीएफएस एक विधि नहीं के साथ, यह उपयोग करने के लिए अतिरिक्त प्रकार हिंट करने का सिर्फ एक सा की आवश्यकता होती है लगता है।

def dfs[G <: Graph](graph : G, nodeAction : G#Node => Unit) = print("dfsing!") 

dfs[ConcreteGraph](new ConcreteGraph, {node => println("foo")}) 

तीसरा तरीका एक करीबी डीएफएस के साथ है। जिस तरह से स्काला के प्रकार निष्कर्ष काम करता है, जो वास्तव में एक क्लीनर इंटरफेस

def dfs[G <: Graph](graph : G)(nodeAction : G#Node => Unit) = print("dfsing!") 

dfs(new ConcreteGraph){node => println("foo")} 
+0

धन्यवाद। हालांकि, मुझे यकीन नहीं है कि मुझे क्लास CGNode क्यों करना होगा और कंक्रीटग्राफ में नोड टाइप करना होगा। मैंने एक छोटा सा उदाहरण बनाया: http://snipt.org/vpk और यह मेरे लिए कार्यात्मक लगता है – jpalecek

+0

और दूसरा एक: इस उदाहरण में, क्या मैं उन प्रकारों को डीएफ को प्रतिबंधित कर सकता हूं, जिनके नोड प्रकार <: आदेश दिया गया है या कुछ? – jpalecek

5

में परिणाम है मैं क्यों इन सभी मापदंडों के लिए आवश्यक हैं नहीं दिख रहा। स्कैला (जावा के विपरीत) में भीतरी कक्षाएं ऐसे प्रकार हैं जो बाह्य वस्तु के विशिष्ट उदाहरण पर निर्भर करती हैं। विशेष रूप से:

trait Graph { 
    trait Node 
    def dfs(n: Node) = println("DFSing!") 
} 

val graphA = new Graph {} 
val nodeA = new graphA.Node {} 
val graphB = new Graph {} 
val nodeB = new graphB.Node {} 
graphA.dfs(nodaA) // prints "DFSing!" 
graphB.dfs(nodeB) // prints "DFSing!" 
graphA.dfs(nodeB) // type mismatch; found: graphB.Node required: graphA.Node 
graphB.dfs(nodeA) // type mismatch; found: graphA.node required: graphB.Node 

अनुमोदित, यदि आप निर्भर प्रकारों पर निर्भर होना चाहते हैं तो आप ग्राफ के बाहर dfs को परिभाषित नहीं कर सकते हैं।