我有一个函数可以计算给定一个简单的 node.id、node.parentId 关联的某些 treeNodes 集合的左右节点值。它非常简单并且效果很好......但是,我想知道是否有更惯用的方法。具体来说,有一种方法可以在不使用一些外部跟踪值的情况下跟踪左/右值,但仍然保持美味的递归。
/*
* A tree node
*/
case class TreeNode(val id:String, val parentId: String){
var left: Int = 0
var right: Int = 0
}
/*
* a method to compute the left/right node values
*/
def walktree(node: TreeNode) = {
/*
* increment state for the inner function
*/
var c = 0
/*
* A method to set the increment state
*/
def increment = { c+=1; c } // poo
/*
* the tasty inner method
* treeNodes is a List[TreeNode]
*/
def walk(node: TreeNode): Unit = {
node.left = increment
/*
* recurse on all direct descendants
*/
treeNodes filter( _.parentId == node.id) foreach (walk(_))
node.right = increment
}
walk(node)
}
walktree(someRootNode)
编辑 - 节点列表取自数据库。将节点拉入适当的树会花费太多时间。我正在将一个平面列表拉入内存,我所拥有的只是通过节点 ID 与父母和孩子相关的关联。
添加左/右节点值允许我通过单个 SQL 查询获得所有子节点(和子节点的子节点)的快照商店。
如果父子关联发生变化(它们经常发生变化),计算需要非常快速地运行以保持数据完整性。
除了使用很棒的 Scala 集合之外,我还通过对树节点上的一些前/后过滤使用并行处理来提高速度。我想找到一种更惯用的方法来跟踪左/右节点值。在查看@dhg 的答案后,它变得更好了。使用 groupBy 而不是过滤器将算法(主要是?)变成线性的而不是二次的!
val treeNodeMap = treeNodes.groupBy(_.parentId).withDefaultValue(Nil)
def walktree(node: TreeNode) = {
def walk(node: TreeNode, counter: Int): Int = {
node.left = counter
node.right =
treeNodeMap(node.id)
.foldLeft(counter+1) {
(result, curnode) => walk(curnode, result) + 1
}
node.right
}
walk(node,1)
}