如何使用 Scala Collections TreeMap(或类似的)维护关于树的 属性
How to maintain a property about trees using a Scala Collections TreeMap (or similar)
由于 scala RedBlack 树在 scala 集合中不再可用,我无法弄清楚如何在 scala 树的节点中维护有关树的属性,因为我需要直接访问树而不是标准集合提供的封装接口。
例如,我想要一棵树,它在每个节点上维护一个字段 x,使得函数 f 对每个节点 n 成立,这样
f(null) = 0
f(n) = n.x + f(n.left) + f(n.right)
我可以为此有效地使用 Scala 标准库 TreeMap 还是我必须实现我自己的树(目前正在这样做)?
遗憾的是,我认为您无法使用 TreeMap 执行此操作。您可能只想从 Scala 集合中复制 the red black tree 并添加一个摘要字段。
或者,您可能想要使用允许指定 "measure" 的 tree-based 集合之一。例如。
https://github.com/Sciss/FingerTree
或者,您可以使用这个可怕的 hack 将一些摘要数据添加到节点,例如一个树集。请注意命名空间 scala.collection.immutable,以便您可以从实用程序内部访问 RedBlackTree。
package scala.collection.immutable
trait Summary[E, S] {
def apply(v: E): S
def empty: S
def combine(a: S, b: S): S
def combine3(a: S, b: S, c: S) = combine(combine(a, b), c)
}
object TreeSetSummarizer {
private[this] val treeSetAccessor = classOf[scala.collection.immutable.TreeSet[_]].getDeclaredField("tree")
treeSetAccessor.setAccessible(true)
private def tree[K](set: TreeSet[K]): AnyRef = treeSetAccessor.get(set) match {
case t: RedBlackTree.Tree[K, Unit] ⇒ t
case _ ⇒ "null"
}
private type JFunction[T, R] = java.util.function.Function[T, R]
def apply[K, S](implicit s: Summary[K, S]): (TreeSet[K] ⇒ S) = new TreeSetSummarizer[K, S]
}
class TreeSetSummarizer[K, S](implicit summary: Summary[K, S]) extends (TreeSet[K] ⇒ S) {
import TreeSetSummarizer._
// this should be a guava cache using weak keys to prevent memory leaks
private val memo = new java.util.IdentityHashMap[AnyRef, S]()
private val f: JFunction[AnyRef, S] = new JFunction[AnyRef, S] {
def apply(t: AnyRef): S = t match {
case t: RedBlackTree.Tree[K, Unit] ⇒ summary.combine3(apply0(t.left), summary.apply(t.key), apply0(t.right))
case _ ⇒ summary.empty
}
}
private def apply0(set: AnyRef): S =
memo.computeIfAbsent(set, f)
def apply(set: TreeSet[K]): S =
apply0(tree(set))
}
下面是您将如何使用它
import scala.collection.immutable._
// create a large TreeSet
val largeSet = TreeSet(0 until 10000: _*)
// define your summary
implicit val s = new Summary[Int, Long] {
def empty = 0L
def apply(x: Int) = x
def combine(a: Long, b: Long) = a + b
}
// define your summarizer. You need to keep the summarizer instance for
// having a performance benefit, since it internally stores summaries for
// tree nodes in an identity hash map
val summary = TreeSetSummarizer.apply[Int, Long]
// summarize something
println(summary(largeSet))
// summarize a modified set. This is fast because the summaries for tree
// nodes are being cached.
val largeSet1 = largeSet - 5000
println(summary(largeSet1))
请注意,考虑到反射和散列开销,这可能最适合用于计算量更大的汇总,而不是简单的求和。
更新:我现在已经编写了一个小型库 persistentsummary 来为现有的 Scala 集合定义持久性摘要。这应该完全符合您的要求。
由于 scala RedBlack 树在 scala 集合中不再可用,我无法弄清楚如何在 scala 树的节点中维护有关树的属性,因为我需要直接访问树而不是标准集合提供的封装接口。
例如,我想要一棵树,它在每个节点上维护一个字段 x,使得函数 f 对每个节点 n 成立,这样
f(null) = 0
f(n) = n.x + f(n.left) + f(n.right)
我可以为此有效地使用 Scala 标准库 TreeMap 还是我必须实现我自己的树(目前正在这样做)?
遗憾的是,我认为您无法使用 TreeMap 执行此操作。您可能只想从 Scala 集合中复制 the red black tree 并添加一个摘要字段。
或者,您可能想要使用允许指定 "measure" 的 tree-based 集合之一。例如。 https://github.com/Sciss/FingerTree
或者,您可以使用这个可怕的 hack 将一些摘要数据添加到节点,例如一个树集。请注意命名空间 scala.collection.immutable,以便您可以从实用程序内部访问 RedBlackTree。
package scala.collection.immutable
trait Summary[E, S] {
def apply(v: E): S
def empty: S
def combine(a: S, b: S): S
def combine3(a: S, b: S, c: S) = combine(combine(a, b), c)
}
object TreeSetSummarizer {
private[this] val treeSetAccessor = classOf[scala.collection.immutable.TreeSet[_]].getDeclaredField("tree")
treeSetAccessor.setAccessible(true)
private def tree[K](set: TreeSet[K]): AnyRef = treeSetAccessor.get(set) match {
case t: RedBlackTree.Tree[K, Unit] ⇒ t
case _ ⇒ "null"
}
private type JFunction[T, R] = java.util.function.Function[T, R]
def apply[K, S](implicit s: Summary[K, S]): (TreeSet[K] ⇒ S) = new TreeSetSummarizer[K, S]
}
class TreeSetSummarizer[K, S](implicit summary: Summary[K, S]) extends (TreeSet[K] ⇒ S) {
import TreeSetSummarizer._
// this should be a guava cache using weak keys to prevent memory leaks
private val memo = new java.util.IdentityHashMap[AnyRef, S]()
private val f: JFunction[AnyRef, S] = new JFunction[AnyRef, S] {
def apply(t: AnyRef): S = t match {
case t: RedBlackTree.Tree[K, Unit] ⇒ summary.combine3(apply0(t.left), summary.apply(t.key), apply0(t.right))
case _ ⇒ summary.empty
}
}
private def apply0(set: AnyRef): S =
memo.computeIfAbsent(set, f)
def apply(set: TreeSet[K]): S =
apply0(tree(set))
}
下面是您将如何使用它
import scala.collection.immutable._
// create a large TreeSet
val largeSet = TreeSet(0 until 10000: _*)
// define your summary
implicit val s = new Summary[Int, Long] {
def empty = 0L
def apply(x: Int) = x
def combine(a: Long, b: Long) = a + b
}
// define your summarizer. You need to keep the summarizer instance for
// having a performance benefit, since it internally stores summaries for
// tree nodes in an identity hash map
val summary = TreeSetSummarizer.apply[Int, Long]
// summarize something
println(summary(largeSet))
// summarize a modified set. This is fast because the summaries for tree
// nodes are being cached.
val largeSet1 = largeSet - 5000
println(summary(largeSet1))
请注意,考虑到反射和散列开销,这可能最适合用于计算量更大的汇总,而不是简单的求和。
更新:我现在已经编写了一个小型库 persistentsummary 来为现有的 Scala 集合定义持久性摘要。这应该完全符合您的要求。