Эскиз возможного решения:
Поместите операцию карты во вспомогательную черту
скажем 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)], но другие реализации графа возвращают что-то полностьюразные.Это похоже на то, что делается в библиотеке коллекций.
Мы можем ввести черту CanMapGraph [From, Elem, To], похожую на CanBuildFrom
trait CanMapGrap[FromGraph, FromElem, ToGraph, ToElem] {
def map(data: FromGraph, f: FromElem => ToElem): ToGraph
}
(возможно, вы бы это изменилииметь больше элементарных операций, чем map, так что его можно использовать для различных операций, как это делается с CanBuildFrom
)
Тогда map будет
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
}