Scala - 展平树结构
Scala - flattening a tree structure
我有一个从 Java 图书馆收到的树结构。我试图将其展平,因为我只对树的 "key" 值感兴趣。树由以下零个或多个组成 类:
class R(val key: String, val nodes: java.util.List[R]) {}
空的 nodes 列表表示分支的结尾。可以通过以下代码构建示例:
val sample = List[R](
new R("1", List[R](
new R("2", List[R]().asJava),
new R("3", List[R](new R("4", List[R]().asJava))
.asJava)).asJava)).asJava
我在编写正确方法和有效方法时遇到问题。这是我目前所拥有的:
def flattenTree(tree: List[R]): List[String] = {
tree.foldLeft(List[String]())((acc, x) =>
x.key :: flattenTree(x.nodes.asScala.toList))
}
然而,当我 运行 这段代码时,尽管它可能效率低下,但我仍然认为它是错误的。我的结果最终是:
>>> flattenTree(sample.asScala.toList)
res0: List[String] = List(1, 3, 4)
这意味着由于某种原因我丢失了键为“2”的节点。
有人可以推荐一种正确且更有效的方法来压平这棵树吗?
您未能在每次连续调用中添加累积的密钥。尝试以下操作:
def flattenTree(tree: List[R]): List[String] = {
tree.foldLeft(List[String]())((acc, x) =>
x.key :: flattenTree(x.nodes.asScala.toList) ++ acc)
}
生成结果:List(1, 3, 4, 2)
,
或者,如果正确排序很重要:
def flattenTree(tree: List[R]): List[String] = {
tree.foldLeft(List[String]())((acc, x) =>
acc ++ (x.key :: flattenTree(x.nodes.asScala.toList)))
}
生成结果:List(1, 2, 3, 4)
将每个R
转换为一个Scalaz Tree
,并调用flatten
进行前序遍历
import scala.collection.JavaConversions._
import scalaz._
def rTree(r: R): Tree[String] =
Tree.node(r.key, r.nodes.toStream.map(rTree))
sample.flatMap(r => rTree(r).flatten): Seq[String]
// List(1, 2, 3, 4)
编辑:不幸的是,由于 a bug in scalaz 从版本 7.1.1 开始,这会导致宽树的堆栈溢出。
您可以定义一个函数来将 R
对象展平为 flatMap
:
// required to be able to use flatMap on java.util.List
import scala.collection.JavaConversions._
def flatten(r: R): Seq[String] = {
r.key +: r.nodes.flatMap(flatten)
}
还有一个函数来展平这些序列:
def flattenSeq(l: Seq[R]): Seq[String] = l flatMap flatten
r.nodes.flatMap(flatten)
是一个 Buffer
,因此在它前面加上效率不高。它变成二次复杂度。因此,如果顺序不重要,则附加更有效:def flatten(r: R): Seq[String] = r.nodes.flatMap(flatten) :+ r.key
像 scalaz
那样使用 Stream
怎么样:
def flatten(rootElem: R): Stream[String] = {
def flatten0(elem: R, xs: Stream[String]): Stream[String] =
Stream.cons(elem.key, elem.nodes.foldLeft(xs)((acc, x) => flatten0(x, acc)))
flatten0(rootElem, Stream.empty)
}
我有一个从 Java 图书馆收到的树结构。我试图将其展平,因为我只对树的 "key" 值感兴趣。树由以下零个或多个组成 类:
class R(val key: String, val nodes: java.util.List[R]) {}
空的 nodes 列表表示分支的结尾。可以通过以下代码构建示例:
val sample = List[R](
new R("1", List[R](
new R("2", List[R]().asJava),
new R("3", List[R](new R("4", List[R]().asJava))
.asJava)).asJava)).asJava
我在编写正确方法和有效方法时遇到问题。这是我目前所拥有的:
def flattenTree(tree: List[R]): List[String] = {
tree.foldLeft(List[String]())((acc, x) =>
x.key :: flattenTree(x.nodes.asScala.toList))
}
然而,当我 运行 这段代码时,尽管它可能效率低下,但我仍然认为它是错误的。我的结果最终是:
>>> flattenTree(sample.asScala.toList)
res0: List[String] = List(1, 3, 4)
这意味着由于某种原因我丢失了键为“2”的节点。
有人可以推荐一种正确且更有效的方法来压平这棵树吗?
您未能在每次连续调用中添加累积的密钥。尝试以下操作:
def flattenTree(tree: List[R]): List[String] = {
tree.foldLeft(List[String]())((acc, x) =>
x.key :: flattenTree(x.nodes.asScala.toList) ++ acc)
}
生成结果:List(1, 3, 4, 2)
,
或者,如果正确排序很重要:
def flattenTree(tree: List[R]): List[String] = {
tree.foldLeft(List[String]())((acc, x) =>
acc ++ (x.key :: flattenTree(x.nodes.asScala.toList)))
}
生成结果:List(1, 2, 3, 4)
将每个R
转换为一个Scalaz Tree
,并调用flatten
进行前序遍历
import scala.collection.JavaConversions._
import scalaz._
def rTree(r: R): Tree[String] =
Tree.node(r.key, r.nodes.toStream.map(rTree))
sample.flatMap(r => rTree(r).flatten): Seq[String]
// List(1, 2, 3, 4)
编辑:不幸的是,由于 a bug in scalaz 从版本 7.1.1 开始,这会导致宽树的堆栈溢出。
您可以定义一个函数来将 R
对象展平为 flatMap
:
// required to be able to use flatMap on java.util.List
import scala.collection.JavaConversions._
def flatten(r: R): Seq[String] = {
r.key +: r.nodes.flatMap(flatten)
}
还有一个函数来展平这些序列:
def flattenSeq(l: Seq[R]): Seq[String] = l flatMap flatten
r.nodes.flatMap(flatten)
是一个 Buffer
,因此在它前面加上效率不高。它变成二次复杂度。因此,如果顺序不重要,则附加更有效:def flatten(r: R): Seq[String] = r.nodes.flatMap(flatten) :+ r.key
像 scalaz
那样使用 Stream
怎么样:
def flatten(rootElem: R): Stream[String] = {
def flatten0(elem: R, xs: Stream[String]): Stream[String] =
Stream.cons(elem.key, elem.nodes.foldLeft(xs)((acc, x) => flatten0(x, acc)))
flatten0(rootElem, Stream.empty)
}