只要递归调用的返回类型为Any,下面的代码就会编译,但显然我做错了,因为它不应该是Any。
case class Group(
id: Long = -1,
parentId: Long = -1,
name: String = "")
def makeTree(groupId: Long, groups: List[Group]) = {
def getAllChildren(gid: Long): Any = {
def children = for {
g <- groups; if g.parentId == gid
} yield g
if (children.isEmpty) List()
else {
children map { x =>
getAllChildren(x.id)
}
}
}
getAllChildren(groupId)
}
val groups = List(Group(1, 0, "A"),
Group(2, 1, "B"),
Group(3, 1, "C"),
Group(4, 2, "D"))
makeTree(1, groups)
//Results in: Any = List(List(List()), List())
}
如果我将getAllChildren的签名更改为:
def getAllChildren(gid: Long): List[Group]
然后我收到一个错误:
type mismatch; found : List[List[Group]] required: List[Group]
我在这里做错了什么。
答案 0 :(得分:3)
不是真的说scala,但看起来,对于某些id,你收集具有该id的组,然后用它的子列表替换每个组,依此类推。
具体来说,这里:
if (children.isEmpty) List()
else {
children map { x =>
getAllChildren(x.id)
}
}
实际上,这是您的错误的根源:您的算法允许无限递归,每次递归在您的返回类型周围添加另一个List[...]
。但是你不能拥有动态深度的类型。
例如,如果您尝试通过提供类型List[List[Group]]
来解决此问题,则会抱怨它找到List[List[List[Group]]]
,依此类推,直到您放弃。
这是类型标记告诉我们我们即将编写潜在无限递归的方式。您可能假定您的层次结构不能涉及循环的不变量,但这不会反映在类型中。事实上,构建两个群体彼此为父母的例子并不难。在这种情况下,您的程序将生成一个更深的嵌套列表,直到它由于内存不足而终止。
请注意,您无法通过使用flatMap而不是map来修复代码:原因是getAllChildren永远不会生成包含Group元素的列表。它要么返回一个空列表,要么返回一个空列表的扁平列表,即一个空列表。因此,如果它应该返回,它将在flatmap版本中返回一个空列表。
答案 1 :(得分:3)
您需要将children map ...
的号码更改为children flatMap ...
,否则您不会返回List[Group]
,但可能会像@Ingo建议的那样List[List[.....]]
。 flatMap
会将每个元素映射到List
,然后展平所有列表,只创建一个包含所有子元素的List[Group]
。
编辑:正如@Ingo指出的那样,即使flatMap
可以解决输入问题,您的功能仍然无效,因为您始终返回一个空列表。你应该去
children flatMap { x => x :: getAllChildren(x.id, acc) }
将孩子置于孩子的孩子身边。
答案 2 :(得分:1)
如果你将children map
上返回的列表展平,那么你的代码就可以了:
def makeTree(groupId: Long, groups: List[Group]) = {
def getAllChildren(gid: Long): List[Group] = {
def children = for {
g <- groups; if g.parentId == gid
} yield g
if (children.isEmpty) List()
else {
val listList = children map { x =>
x :: getAllChildren(x.id)
}
listList.flatten
}
}
getAllChildren(groupId)
}
发生这种情况的原因是您正在获取List [Group]并将其映射到也返回List[Group]
的函数,从而产生List[List[Group]]
。简单地展平List将产生所需的结果。