我怎样才能处理"展平"递归定义的类型结构?

时间:2014-06-22 03:43:49

标签: scala scala-collections

考虑这些案例类,其中Blog包含Post个,Post包含Comment s,然后Relation将单个父级与a {0}}相关联单身孩子:

case class Comment(id: Int)
case class Post(id: Int, comments: List[Comment])
case class Blog(id: Int, posts: List[Post])

case class Relation[A, B](parent: A, child: B)

我要做的是递归地将这些Relations的列表折叠到顶层parent,从这样的东西开始(这个非嵌套结构的原因)首先,这是解析时带连接列的SQL结果的样子:

val relations = List(
    Relation(Post(1, Nil), Comment(1)),
    Relation(Post(1, Nil), Comment(2)),
    Relation(Post(2, Nil), Comment(3)),
    Relation(Post(3, Nil), Comment(4)),
    Relation(Post(4, Nil), Comment(5))
)

输出应为:

List(
    Post(1,List(Comment(1), Comment(2))),
    Post(2, List(Comment(3), Comment(4), Comment(5)))
)

使用一个嵌套级别,这很容易:

def collapse[A, B](relations: List[Relation[A, B]])(implicit f: (A, List[B]) => A): List[A] = {
    relations.groupBy(_.parent)
       .mapValues(_.map(_.child))
       .toList
       .map(f.tupled)
}

implicit函数需要的位置如下:

implicit def collapsePost(post: Post, comments: List[Comment]): Post = post.copy(comments = comments)

当我想添加另一层嵌套时,麻烦就开始了,B可能会成为另一个Relation。所以我尝试了很多这种结构的变体:

sealed abstract class Node
case class Relation[A, B <: Node](parent: A, child: B) extends Node
case class Child[B](value: B) extends Node

所以现在我试图崩溃的例子看起来更像是这样:

val relations = List(
    Relation(Blog(1, Nil), Relation(Post(1, Nil), Child(Comment(1)))),
    Relation(Blog(1, Nil), Relation(Post(2, Nil), Child(Comment(2)))),
    Relation(Blog(1, Nil), Relation(Post(2, Nil), Child(Comment(3))))
)

所需的输出:

List(
    Blog(1, List(
           Post(1, List(Comment(1))),
           Post(2, List(Comment(2), Comment(3)))
        )
     )
)

我的collapse功能的签名保持不变(暂时)。我可以在parent参数的relations上进行分组,但后来我陷入了递归步骤。在collapse中的函数链中的某个点,我将Map[A, List[Node]],其中List[Node]List[Relation[B, ?]]List[Child[B]]。问号说明我未能继续前进。我已尝试将TypeTag引入模式匹配列表,因为类型已被删除,但编译器仍会抱怨Relation的第二个类型参数。

即。如果listList[Node],那么

list match {
    case rel @ List[Relation[B, _]] => collapse(rel) // Complains about missing TypeTag in recursive call to `collapse`
    case children @ List[Child[B]] => children
} 

在这样的递归定义的类中是否存在丢失类型参数的方法(如果可能,不需要反射)?或许我完全走错了路。

1 个答案:

答案 0 :(得分:1)

这是一个不使用Child类或需要更改collapse的解决方案:

implicit def collapseBlog(blog: Blog, relations: List[Relation[Post, Comment]])(implicit f: (Post, List[Comment]) => Post) = {
  blog.copy(posts = collapse(relations))
}

我们正在利用递归隐式解析 - collapseBlog将使用collapsePost作为其隐式,并且它们可以一起折叠两级深层结构。基本上,collapse(relations)在隐式解决后变为collapse(relations)((x, y) => collapseBlog(x, y)(collapsePost))