2011-07-30 65 views
5

背景信息:我目前正試圖建立一個通用圖庫,其中包括幾種不同的搜索算法(我已經開始使用Dijkstra的)。我已經建立了幾個特徵來表示會在某些類型的圖表中找到方法(如權重,指導):斯卡拉未能推斷出正確的類型參數

trait GraphOps[V,E] { ... } 
trait WeightedGraphOps[V,E] extends GraphOps[V,E] { ... } 
trait DirectedGraphOps[V,E] extends GraphOps[V,E] { ... } 
object GraphOps{ 
    def Dijkstra[V,E,G <: WeightedGraphOps[V,E] with DirectedGraphOps[V,E]](graph:G, start:V) = { ... } 
} 

在其他地方,我有一個類的具體實現加權,有向圖的我想上運行Dijkstra算法:

class GraphMap[T](...) 
extends scala.collection.mutable.Map[Position,T] 
with WeightedGraphOps[Position,Edge] with DirectedGraphOps[Position,Edge] { ... } 

但是,當我試圖測試一下:

val graph = new GraphMap[Int](...) 
val (dist, prev) = GraphOps.Dijkstra(graph, Position(0,0)) 

問題:在編譯期間我收到以下錯誤:error: inferred type arguments [com.dylan.data.Position,Nothing,com.dylan.data.GraphMap[Int]] do not conform to method Dijkstra's type parameter bounds [V,E,G <: com.dylan.data.WeightedGraphOps[V,E] with com.dylan.data.DirectedGraphOps[V,E]]
我花了很長時間才發現它推斷我的Edge(E)類型爲Nothing,但我不明白爲什麼它無法成功推斷它應該是Edge。爲什麼它無法推斷該類型參數,我該如何解決它?

P.S.我試着做以下,並得到它的工作,但是這似乎可怕的不方便什麼應該是一個方便的方法:

type Helpful = WeightedGraphOps[Position,Edge] with DirectedGraphOps[Position,Edge] 
val (dist, prev) = GraphOps.Dijkstra[Position,Edge,Helpful](graph, Position(0,0)) 
+0

你知道這裏有一個圖庫,不是嗎? –

+0

因爲我這樣做是爲了愛好,而且因爲幾分鐘的四處搜索並沒有導致明確的答案,所以我想我可能會把自己放在一起。 – Dylan

+0

我很好奇你什麼條件,你完全谷歌?我試過'scala圖庫',並得到了這個:http://code.google.com/p/scala-graphs/對你來說不是這樣嗎? – AndreasScheinert

回答

6

Daniel可能是對的,現有的Scala類型推理器需要更多的直接信息來找出那個E必須是Edge。另外,我的理解是類型推斷故意被指定爲未來的改進。

無論如何,我認爲你可以採取另一種方法來解決類型推斷問題:使用類型成員而不是參數。我已經用下面的自包含代碼說明了我的意思。關鍵想法是類型EV成爲GraphOps類型的一部分,但它們仍然可以通過使用類型改進(如在Dijkstra方法中)作爲類型參數來浮現。

trait GraphOps { type E; type V } 
trait WeightedGraphOps extends GraphOps { } 
trait DirectedGraphOps extends GraphOps { } 
object GraphOps{ 
    def Dijkstra[V0, G <: (WeightedGraphOps{type V = V0}) 
         with (DirectedGraphOps{type V = V0})] 
     (graph:G, start:V0) = { } 
} 

case class Position(x: Int, y: Int) 
case class Edge() 

case class GraphMap[T]() extends WeightedGraphOps with DirectedGraphOps { 
    type E = Edge 
    type V = Position 
} 

object Test { 
    val graph = new GraphMap[Int]() 
    GraphOps.Dijkstra(graph, Position(0,0)) 
} 

編輯一個這種類型的部件的方法的潛在的限制是在方法Dijkstra該穿類型參數G較少的約束。具體而言,邊界WeightedGraphOpsDirectedGraphOps不限於具有相同類型的成員E。我不知道如何解決這個問題,而不會碰到你最初報告的類型推斷問題。一種方法是在這個問題中的模式:Why do these type arguments not conform to a type refinement?,但似乎Scala編譯器無法處理它。

編輯2忽略上述段落。正如迪倫在評論中提到的,對於這種diamond inheritance的情況,斯卡拉很好地確保了E類型的一致性。例如,下面的代碼編寫得很好:

trait GraphOps { type E; type V } 
trait WeightedGraphOps extends GraphOps { def f(e: E) } 
trait DirectedGraphOps extends GraphOps { def e: E } 
object GraphOps{ 
    def Dijkstra[V0, G <: (WeightedGraphOps{type V = V0}) with (DirectedGraphOps{type V = V0})] (graph:G, start:V0) = { 
    graph.f(graph.e) 
    } 
} 
+0

這幫了我,謝謝!我不確定是否應該擔心邊緣類型約束的缺失 - 我可能錯過了某些內容,但我無法創建一個混合了Ops類型但具有不同'E'類型的對象。它甚至有可能嗎? – Dylan

+0

有趣。這聽起來像鑽石問題(http://en.wikipedia.org/wiki/Diamond_problem)的類型。對於數值,Scala對鑽石問題的解決方案是基於線性化方案的一個值覆蓋另一個值。對於類型,我不驚訝這是不允許的,以避免不一致。 –

2

爲什麼應該是Edge?如果您查看Dijkstra的聲明,您會看到沒有參數提及E(graph:G, start:V)。所以斯卡拉有一個線索G應該是什麼,以及V應該是什麼。沒有參考E的參數。

+0

但是不能有足夠聰明的Scala編譯器在此調用站點找出它嗎?知道'graph'具有類型'WeightedGraphOps [Position,Edge] ...'基本上修復了'E'必須使類型工作。 –

+0

它應該是'Edge',因爲這是GraphMap中邊緣的類型。 Dijkstra's的實施需要這種類型,所以我不能把它排除在外。 – Dylan

+0

@Dylan我明白你有一個問題,我只是想讓你更好地理解類型推斷約束,所以你會更容易編寫代碼。這裏的問題是:類型是從參數中推斷出來的。 'G'是從'graph'推斷出來的。什麼是參數'E'應該推斷出來?它不會從類型約束中推斷出來 - 這正是它所檢查的內容,從它推斷出的內容。 –