私は単純なnode.id、node.parentIdアソシエーションを指定してtreeNodeのコレクションの左右のノード値を計算する関数を持っています。それは非常に簡単で十分にうまくいきます...しかし、もっと慣用的なアプローチがあるのだろうかと思います。具体的には、いくつかの外部的に追跡された値を使わずに左/右の値を追跡する方法がありますが、やはり美しい再帰を保ちます。このメソッドをもっともっとScalaliciousにするには
/*
* 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を使った関連付けです。
左/右のノード値を追加すると、1つのSQLクエリですべての子(および子どもの子)のスナップショットを取得できます。
親子関係が変更された場合(非常に頻繁に実行される)、データの整合性を維持するために、計算を非常に迅速に実行する必要があります。
すばらしいScalaコレクションを使用することに加えて、ツリーノードのいくつかの前後のフィルタ処理に並列処理を使用することでスピードも向上しました。私は、左/右のノード値を追跡するもっと慣用的な方法を見つけたいと思っていました。 @dhgからの答えを見た後、それはさらに良くなった。フィルタの代わりにgroupByを使用すると、アルゴリズム(大部分は?)が2次ではなく線形に変わります!
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)
}
再帰的にTreeNodeを定義しない理由がありますか? 'walktree'のポイントは何ですか? 'left'と' right'の値を再設定するには? 'left'と' right'値が 'TreeNode'に関連付けられていないのはなぜですか? – dhg
これはcodereviewではありませんが、コメント数が少なくて済み、少なくとも1つの有益なコメントが必要です。あなたのコメントの正確にゼロは、コードがまだあなたに伝えていないことに関係するものを言う。それらをすべて投げ捨て、ゴールを説明する2行を追加します。 –
@Rex、私は同じ考えを持っていました:-) – dhg