zig*_*tar 7 types functional-programming scala scalaz
假设我有一个类型类Graph[G,V],它声明类型的对象G也是一个顶点类型的图形V.
现在我有一个隐含的,让我将类型对的集合A视为具有类型顶点的图形A(不能表达未连接的顶点......).我可以通过导入以下对象的范围来使用隐式.
object TupleSetGraph{
implicit def ts2graph[A]: Graph[Set[(A,A)],A] = new Graph[Set[(A,A)],A] {
def nodes(g: Set[(A, A)]): Set[A] = g flatMap (t => Set(t._1,t._2))
def adjacent(g: Set[(A, A)], n1: A, n2: A): Boolean = g.contains((n1,n2)) || g.contains((n2,n1))
}
}
Run Code Online (Sandbox Code Playgroud)
假设我还希望能够映射顶点的内容,从而能够执行以下操作:
(_: Set[(A,A)]).map((_: A => B)): Set[(B,B)]
Run Code Online (Sandbox Code Playgroud)
但是已经map定义了Set.如何处理同一数据结构可以被视为同一事物(具有某种map功能的东西)的问题?
画出一个可能的解决方案:
说GraphOps(这可能是Graph它本身,但地图签名可能太复杂了)
case class GraphOps[G](data: G) { def map...}
Run Code Online (Sandbox Code Playgroud)
轻松获得GraphOps:
object Graph {
def apply[G](data: G) = GraphOps(data)
}
Run Code Online (Sandbox Code Playgroud)
这样,调用将是
Graph(set).map(f)
Run Code Online (Sandbox Code Playgroud)
apply可以隐式进行,但我不确定我想这样做(如果我这样做,我不确定它能否正确找到地图)。
我们也可以做
case class GraphOps[G,V](data: G, graph: Graph[G,V])
Run Code Online (Sandbox Code Playgroud)
和
object Graph {
def apply[G,V](data: G)(implicit graph: Graph[G,V]) = GraphOps(data, graph)
}
Run Code Online (Sandbox Code Playgroud)
这样做的好处是VGraphOps 中可以使用顶点类型
您想要的签名很复杂,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
}
Run Code Online (Sandbox Code Playgroud)
(可能您会更改它以具有比 map 更多的基本操作,以便它可以用于不同的操作,就像使用 所做的那样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)
}
Run Code Online (Sandbox Code Playgroud)
您可以定义
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))}
}
Run Code Online (Sandbox Code Playgroud)
然后你就这么做了
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))
Run Code Online (Sandbox Code Playgroud)
这样做的一个问题是,第一个参数列表(即 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
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
198 次 |
| 最近记录: |