Scala 如何使此方法更具规模性
我有一个函数,在给定一个简单的node.id、node.parentId关联的情况下,计算某些树节点集合的左右节点值。它非常简单,而且效果也很好……但是,我想知道是否有更惯用的方法。具体来说,有一种方法可以跟踪左/右值,而不使用一些外部跟踪的值,但仍然保持美味的递归Scala 如何使此方法更具规模性,scala,recursion,idioms,Scala,Recursion,Idioms,我有一个函数,在给定一个简单的node.id、node.parentId关联的情况下,计算某些树节点集合的左右节点值。它非常简单,而且效果也很好……但是,我想知道是否有更惯用的方法。具体来说,有一种方法可以跟踪左/右值,而不使用一些外部跟踪的值,但仍然保持美味的递归 /* * A tree node */ case class TreeNode(val id:String, val parentId: String){ var left: Int = 0 var right
/*
* 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)
}
如果我正确获得了您的算法:
def walktree(node: TreeNode, c: Int): Int = {
node.left = c
val c2 = treeNodes.filter(_.parentId == node.id).foldLeft(c + 1) {
(cur, n) => walktree(n, cur)
}
node.right = c2 + 1
c2 + 2
}
walktree(new TreeNode("", ""), 0)
可能会出现一个接一个的错误
很少有随意的想法(更适合):
- 尝试发布编译。。。我们必须猜测这是一个
序列:TreeNode
对于val
类是隐式的:case
case class TreeNode(val id: String, val parentId: String) {
- 对于
功能,避免显式单元
和=
:单元
def walktree(node: TreeNode) = { def walk(node: TreeNode): Unit = {
- 有副作用的方法应该有
:()
- 更简洁的语法应该是
:treeNodes foreach walk
treeNodes foreach (walk(_))
treeNodes filter (_.parentId == node.id) foreach (walk(_))
折叠
,它将当前值向下传递,并将更新后的值向上传递。请注意,在walktree
之前执行treeNodes.groupBy(u.parentId)
也可能是值得的,以防止每次调用walk
时调用treeNodes.filter(…)
val treeNodes = List(TreeNode("1","0"),TreeNode("2","1"),TreeNode("3","1"))
val treeNodeMap = treeNodes.groupBy(_.parentId).withDefaultValue(Nil)
def walktree2(node: TreeNode) = {
def walk(node: TreeNode, c: Int): Int = {
node.left = c
val newC =
treeNodeMap(node.id) // get the children without filtering
.foldLeft(c+1)((c, child) => walk(child, c) + 1)
node.right = newC
newC
}
walk(node, 1)
}
它产生了同样的结果:
scala> walktree2(TreeNode("0","-1"))
scala> treeNodes.map(n => "(%s,%s)".format(n.left,n.right))
res32: List[String] = List((2,7), (3,4), (5,6))
也就是说,我将完全重写您的代码,如下所示:
case class TreeNode( // class is now immutable; `walktree` returns a new tree
id: String,
value: Int, // value to be set during `walktree`
left: Option[TreeNode], // recursively-defined structure
right: Option[TreeNode]) // makes traversal much simpler
def walktree(node: TreeNode) = {
def walk(nodeOption: Option[TreeNode], c: Int): (Option[TreeNode], Int) = {
nodeOption match {
case None => (None, c) // if this child doesn't exist, do nothing
case Some(node) => // if this child exists, recursively walk
val (newLeft, cLeft) = walk(node.left, c) // walk the left side
val newC = cLeft + 1 // update the value
val (newRight, cRight) = walk(node.right, newC) // walk the right side
(Some(TreeNode(node.id, newC, newLeft, newRight)), cRight)
}
}
walk(Some(node), 0)._1
}
然后你可以这样使用它:
walktree(
TreeNode("1", -1,
Some(TreeNode("2", -1,
Some(TreeNode("3", -1, None, None)),
Some(TreeNode("4", -1, None, None)))),
Some(TreeNode("5", -1, None, None))))
制作:
Some(TreeNode(1,4,
Some(TreeNode(2,2,
Some(TreeNode(3,1,None,None)),
Some(TreeNode(4,3,None,None)))),
Some(TreeNode(5,5,None,None))))
treeNodes
在哪里定义?你不递归定义TreeNode有什么原因吗?walktree的要点是什么?要对左
和右
值重新编号?为什么左
和右
值不与TreeNode
s关联?这不是codereview,但您需要的注释要少得多,并且至少需要一条有用的注释。在您的注释中,完全没有任何代码没有告诉您的相关内容。扔掉它们,再加上两行描述目标的文字。@Rex,我有同样的想法:-)treeNodes只是一个TreeNode列表。它不是递归定义的,因为列表是从数据库中提取的,这需要时间。+1对于充分理解所需内容以找到解决方案来说,它实际上比黄金更好。这会使节点行走减少半秒!谢谢@尼尔,这是因为在你的版本中,treeNodes.filter(…)
必须在每次调用walk
查找子节点时遍历每个节点。在我的版本中,节点确切地知道它的子节点在哪里,并直接调用它们。我的意思是使用foldLeft的过滤器版本。如果我把树组装成一个节点结构,我就不需要走树了(我可能会在组装节点时保持跟踪)。过滤器是必需的,因为我只有一个从数据库中提取并将持久化到数据库的内存中的平面列表。@尼尔,我添加了一个groupBy
,防止每次调用walk
时都必须filter
。
Some(TreeNode(1,4,
Some(TreeNode(2,2,
Some(TreeNode(3,1,None,None)),
Some(TreeNode(4,3,None,None)))),
Some(TreeNode(5,5,None,None))))