
.13.e.source-code.alt-tree.scala Maven / Gradle / Ivy
/*
Copyright 2010 Aaron J. Radke
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cc.drx.alt
//TODO generalize this type of tree with a path instead of a branch
//where the index type of the zipper is an Path[Boolean] => BinaryTree, Path[Int] => RoseTree, Path[A] => MapTree
object Branch{
def apply[A](node:A):Branch[A] = new Branch[A](node :: Nil)
def empty[A]:Branch[A] = new Branch[A](List.empty[A])
}
case class Branch[A](stack:List[A]) extends AnyVal{
def /(node:A):Branch[A] = new Branch[A](node :: stack)
def isEmpty = stack.isEmpty
def head = stack.head
def tail = new Branch[A](stack.tail)
def reverse = new Branch[A](stack.reverse)
override def toString = stack.reverse.mkString(" / ")
}
object Tree{
private def inverted[A](branch:Branch[A]):Tree[A] = {
if(branch.isEmpty) new Tree[A](Map())
else new Tree[A](
Map(branch.head -> inverted(branch.tail))
)
}
def apply[A](branch:Branch[A]):Tree[A] = inverted(branch.reverse)
def apply[A]() = empty[A]
def empty[A] = new Tree[A](Map.empty[A,Tree[A]])
}
case class Tree[A](nodes:Map[A,Tree[A]]){
override def toString:String = nodes.map{case (k,v) =>
if(v.isEmpty) k.toString else k.toString + ":" + v.toString
}.mkString("Tree(",", ",")")
def isEmpty = nodes.isEmpty
def nonEmpty = nodes.nonEmpty
/**optimized version of startsWith(branch.reverse)*/
@deprecated("confusing meanings use containsReverse instead","v0.2.0")
def startsWithInverted(branch:Branch[A]):Boolean = {
if(nodes.isEmpty) true // an empty tree always matches ???
else if(branch.isEmpty) false
else nodes get branch.head map {_ startsWithInverted branch.tail} getOrElse false
}
@deprecated("confusing meanings use contains instead","v0.2.0")
def startsWith(branch:Branch[A]):Boolean = startsWithInverted(branch.reverse)
private def containsReverse(branch:Branch[A], allowClipping:Boolean):Boolean = {
if(allowClipping && isEmpty) true else
branch.stack match {
case Nil => true // the non branch is in every tree
case root :: Nil => nodes contains root //if the root node is in the tree
case root :: ns => nodes.get(root) map {_.containsReverse(Branch(ns),allowClipping)} getOrElse false
}
}
/**does a tree contain a branch? (a tree can be larger than a branch) */
def contains(branch:Branch[A]):Boolean = containsReverse(branch.reverse, false)
/**optimized version of contains(branch.reverse)*/
def containsReverse(branch:Branch[A]):Boolean = containsReverse(branch, false)
/**does a tree support a larger branch (ie the tree is a subset of the branch?
* this is a common question for fast indexing and lookups into tree keys and data
*/
def supports(branch:Branch[A]):Boolean = nonEmpty && containsReverse(branch.reverse, true)
/**optimized version of contains(branch.reverse)*/
def supportsReverse(branch:Branch[A]):Boolean = nonEmpty && containsReverse(branch, true)
/**add branch*/
def +(branch:Branch[A]):Tree[A] = this ++ Tree(branch)
/**merge trees*/
def ++(that:Tree[A]):Tree[A] = new Tree[A](
that.nodes.keys.foldLeft(this.nodes){ case (hash, k) =>
val stepFamily = if(hash contains k) hash(k) ++ that.nodes(k) else that.nodes(k)
hash + (k -> stepFamily) //fold in the renamed step family as the clobber function
}
)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy