考えられる解決策のスケッチ:
マップ操作を補助特性に入れます
言うGraphOps
(Graph
それ自体かもしれませんが、マップの署名はおそらくそれには複雑すぎるでしょう)
case class GraphOps[G](data: G) { def map...}
簡単に入手できるようにするGraphOps
:
object Graph {
def apply[G](data: G) = GraphOps(data)
}
それで、呼び出しは
Graph(set).map(f)
apply
暗黙的にすることもできますが、それを実行したいかどうかはわかりません(実行した場合、マップが適切に検出されるかどうかはわかりません)。
変異体。GraphOpsにグラフを作成する
私たちもできる
case class GraphOps[G,V](data: G, graph: Graph[G,V])
と
object Graph {
def apply[G,V](data: G)(implicit graph: Graph[G,V]) = GraphOps(data, graph)
}
その良い点は、頂点タイプV
がGraphOpsで利用できることです。
マップ操作の定義
必要な署名は複雑で、Set [(A、A)]はSet [(B、B)]を返しますが、他のグラフ実装はまったく異なるものを返します。これは、コレクションライブラリで行われることと似ています。
CanBuildFromに似た特性CanMapGraph[From、Elem、To]を導入する場合があります
trait CanMapGrap[FromGraph, FromElem, ToGraph, ToElem] {
def map(data: FromGraph, f: FromElem => ToElem): ToGraph
}
(おそらく、これをマップよりも多くの基本操作を持つように変更して、で行われるように、さまざまな操作に使用できるようにしますCanBuildFrom
)
次に、マップは
case class GraphOps[G](data: G) {
def map[A,B](f: A, B)(implicit ev: CanMapFrom[G, A, B, G2]) : G2 =
ev.map(data, f)
}
あなたは定義することができます
implicit def mapPairSetToPairSet[A, B] =
new CanMapGraph[Set[(A,A)], A, Set[(B,B)], B] {
def map(set: Set[(A,A)], f: A => B) = set.map{case (x, y) => (f(x), f(y))}
}
そして、あなたは
val theGraph = Set("A" -> "B", "BB" -> "A", "B" -> "C", "C" -> "A")
Graph(theGraph).map(s: String -> s(0).toLower)
res1: Set[(Char, Char)] = Set((a,b), (b,a), (b,c), (c,a))
それに関する問題は、頂点のタイプが最初の引数リスト(fの引数リスト)で不明であるため、s:Stringで明示する必要があることです。
GraphOps
頂点タイプを早期に取得する代替案でA
は、Mapのパラメーターではなく、のパラメーターであるGraphOps
ため、最初から既知であり、で明示的にする必要はありませんf
。そのようにすると、グラフをのメソッドに渡すことができmap
ますCanMapGraph
。
最初の解決策では、グラフをに与えるのは簡単CanMapGraph
です。
implicit def anyGraphToSet[G,V,W](implicit graph: Graph[G,V])
= new CanMapFrom[G, V, Set[(W,W)], W] {
def map(data: G, f: V => W) =
(for {
from <- graph.nodes(data)
to <- graph.nodes(data))
if graph.adjacent(data, from, to) }
yield (from, to)).toSet
}