In Scala (2.10), I'd like an immutable SeqLike collection (supporting indexing) which offers a SetLike interface to the user, and won't allow duplicate elements. Ideally, this would implement both SetLike and SeqLike, but this isn't possible, so I have to pick one. My first idea was as follows:
sealed class IndexableSet[A] ( private val values : Seq[A] )
extends Set[A]
with SetLike[A,IndexableSet[A]]
{
override def empty : IndexableSet[A] = new IndexableSet[A]( Seq[A]() )
def + ( elem : A ) : IndexableSet[A] = values.contains( elem ) match
{
case true => this
case false => new IndexableSet[A]( values :+ elem )
}
def - ( elem : A ) : IndexableSet[A] = values.contains( elem )
{
case true => new IndexableSet[A]( values.filter( _ != elem ) )
case false => this
}
def iterator = values.iterator
def contains( elem : A ) = values.contains( elem )
def apply( index : Int ) = values( index )
def length : Int = values.size
def contents : Seq[A] = values
}
This exposes a suitable interface, but not sortability (no sortBy or sorted)
I'm wondering, therefore, whether to change my implementation to something which implements Seq and SeqLike instead, and fakes the Set interface:
sealed class UniqueSeq[A] private ( private val values : IndexedSeq[A] )
extends SeqLike[A,UniqueSeq[A]]
with Seq[A]
with GenericTraversableTemplate[A,UniqueSeq]
{
def apply( idx : Int ) : A = values( idx )
def iterator = values.iterator
def length = values.length
override def companion: GenericCompanion[UniqueSeq] = new GenericCompanion[UniqueSeq]()
{
def newBuilder[A]: Builder[A, UniqueSeq[A]] = new Builder[A, UniqueSeq[A]]
{
val elems = new ArrayBuffer[A]()
def +=(a:A) = { elems += a; this }
def clear() { elems.clear }
def result(): UniqueSeq[A] = new UniqueSeq[A](elems)
}
}
def + ( elem : A ) : UniqueSeq[A] = values.contains( elem ) match
{
case true => this
case false => new UniqueSeq[A]( values :+ elem )
}
def - ( elem : A ) : UniqueSeq[A] = values.contains( elem ) match
{
case true => new UniqueSeq[A]( values.filter( _ != elem ) )
case false => this
}
}
I'm not sure which is better - or whether there's another way. I know there are things like TreeSet but the SortedSet trait doesn't offer the critical indexability.
So the questions are:
Is there a clear winner between these two implementations?
Is there another way, which is better, in the standard collections?
I would create a SeqLike backed by a Seq. Override any functions which add elements to add the element to the underlying Seq followed by a call to distinct to eliminate duplicates.
I'd prefer the second solution, possibly modified to maintain both a Set and a Seq internally:
sealed class UniqueSeq[A] private ( values : IndexedSeq[A], valueSet : Set[A] )
extends SeqLike[A,UniqueSeq[A]]
with Seq[A]
with GenericTraversableTemplate[A,UniqueSeq]
{
def apply( idx : Int ) : A = values( idx )
def iterator = values.iterator
def length = values.length
override def companion: GenericCompanion[UniqueSeq] = new GenericCompanion[UniqueSeq]()
{
def newBuilder[A]: Builder[A, UniqueSeq[A]] = new Builder[A, UniqueSeq[A]]
{
val elems = new ArrayBuffer[A]()
def +=(a:A) = { elems += a; this }
def clear() { elems.clear }
def result(): UniqueSeq[A] = new UniqueSeq[A](elems)
}
}
def + ( elem : A ) : UniqueSeq[A] = valueSet.contains( elem ) match
{
case true => this
case false => new UniqueSeq[A]( values :+ elem, valueSet + elem )
}
def - ( elem : A ) : UniqueSeq[A] = valueSet.contains( elem ) match
{
case true => new UniqueSeq[A]( values.filter( _ != elem ), valueSet - elem )
case false => this
}
}
The following code works in my application. It mixes your code with code from this question:
Create a custom scala collection where map defaults to returning the custom collection?
class Unique[A] private (list: Vector[A], set: Set[A]) extends Traversable[A]
with TraversableLike[A, Unique[A]]
with GenericTraversableTemplate[A, Unique]
{
def apply(index: Int): A = list(index)
override def companion: GenericCompanion[Unique] = Unique
def foreach[U](f: A => U) { list foreach f }
override def seq = list
def +(elem: A): Unique[A] = {
if (set.contains(elem)) this
else new Unique(list :+ elem, set + elem)
}
def -(elem: A): Unique[A] = {
if (set.contains(elem)) new Unique(list.filter(_ != elem), set - elem)
else this
}
def --(elems: Traversable[A]): Unique[A] = {
val set = elems.toSet
val values2 = list.filterNot(set.contains)
new Unique(values2, values2.toSet)
}
def ++(elems: Traversable[A]): Unique[A] = {
val list2 = elems.filterNot(set.contains)
val values2 = list ++ list2
new Unique(values2, values2.toSet)
}
def contains(elem: A): Boolean = set.contains(elem)
def zipWithIndex: Traversable[(A, Int)] = list.zipWithIndex
}
object Unique extends TraversableFactory[Unique] {
def newBuilder[A] = new UniqueBuilder[A]
implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Unique[A]] = {
new CanBuildFrom[Coll, A, Unique[A]] {
def apply(): Builder[A, Unique[A]] = new UniqueBuilder()
def apply(from: Coll): Builder[A, Unique[A]] = apply()
}
}
class UniqueBuilder[A] extends Builder[A, Unique[A]] {
private val list = Vector.newBuilder[A]
private val set = new HashSet[A]()
def += (elem: A): this.type = {
if (!set.contains(elem)) {
list += elem
set += elem
}
this
}
def clear() {
list.clear()
set.clear()
}
def result(): Unique[A] = new Unique(list.result, set.toSet)
}
}
Related
I was kinda just pegging away at this problem for fun and kinda hit a road block with it and couldn't find much information on it online. I want to create an algorithm in Scala where when you remove a node of some value from a tree, it will return the set of trees it creates. For example the Tree
1
/ \
3 4
/\ /\
4 5 6 2
If we remove 4 then it would return trees as follows:
[ 1 , 6 , 2 ]
[ / ]
[ 3 ]
[ \ ]
[ 5 ]
What I have so far is as follows:
object TreeStructure {
trait Tree {
def isEmpty(): Boolean
def equals(other: Tree): Boolean
def diff(other: Tree): Tree
def remove(value: Int): List[Tree]
def removeNode(value: Int): Tree
}
case class Node(var left: Tree, var right: Tree, value: Int) extends Tree {
override def isEmpty = false;
override def equals(other: Tree): Boolean = other match {
case x: Node => this.value == x.value && this.left.equals(x.left) && this.right.equals(x.right)
case _ => false
}
override def diff(other: Tree): Tree = {
if (this.equals(other)) new EmptyNode()
else new Node(this.left.diff(other), this.right.diff(other), this.value)
}
override def removeNode(value: Int): Tree = {
if (this.value == value) {
EmptyNode()
} else {
new Node(this.left.removeNode(value),this.right.removeNode(value), this.value)
}
}
override def remove(value: Int): List[Tree] = {
(List(this.removeNode(value)) ++ left.remove(value) ++ right.remove(value))
}
}
case class Leaf(var value: Int) extends Tree {
override def isEmpty(): Boolean = false
override def equals(other: Tree): Boolean = other match {
case y: Leaf => this.value == y.value
case _ => false
}
override def diff(other: Tree): Tree = {
if (this.equals(other)) EmptyNode();
else this
}
override def remove(value: Int): List[Tree] = {
if(this.value == value) Nil
else List(this)
}
override def removeNode(value:Int): Tree = {
if(this.value == value) EmptyNode()
else this
}
}
case class EmptyNode() extends Tree {
override def isEmpty(): Boolean = true
override def equals(other: Tree): Boolean = other match {
case x: EmptyNode => true
case _ => false
}
override def diff(other: Tree): Tree = new EmptyNode()
override def remove(value: Int): List[Tree] = Nil
override def removeNode(value:Int): Tree = new EmptyNode()
}
val t1 = new Node(new Leaf(15), new Leaf(13), 0)
val t2 = new Node(new Leaf(15), new Leaf(13), 0)
val t3 = new Node(new Node(new Leaf(3), new Leaf(13), 0), new Leaf(25), 30)
val t4 = new Leaf(13)
val boo = t1.equals(t2)
t3.diff(t1)
t3.remove(30)
}
I'm relatively newer to Scala, any help would be appreciated.
Cheers
Here is an answer which could probably be written more succinctly, but I hope it somewhat clearly expresses the algorithm:
case class N[T](value: T, children: List[N[T]] = Nil) {
def remove(t: T) = {
val r = _remove(t)
r._1.toList ++ r._2
}
def _remove(t: T): (Option[N[T]], List[N[T]]) =
if(value == t) (None, children.map(_._remove(t)).flatMap{ case (c, o) => c.toList ++ o})
else {
val results = children.map(_._remove(t))
val me = this.copy(children = results.flatMap(_._1))
val others = results.flatMap(_._2)
(Some(me), others)
}
}
Here is an example from the stairway book:
object Example1 {
import collection._
class PrefixMap[T]
extends mutable.Map[String, T]
with mutable.MapLike[String, T, PrefixMap[T]] {
var suffixes: immutable.Map[Char, PrefixMap[T]] = Map.empty
var value: Option[T] = None
def get(s: String): Option[T] = {
// base case, you are at the root
if (s.isEmpty) value
// recursive
else suffixes get (s(0)) flatMap (_.get(s substring 1))
}
def iterator: Iterator[(String, T)] = {
(for (v <- value.iterator) yield ("", v)) ++
(for ((chr, m) <- suffixes.iterator; (s, v) <- m.iterator) yield (chr +: s, v))
}
def +=(kv: (String, T)): this.type = {
update(kv._1, kv._2)
this
}
def -=(key: String): this.type = {
remove(key)
this
}
def withPrefix(s: String): PrefixMap[T] = {
if (s.isEmpty) this
else {
val leading = s(0)
suffixes get leading match {
case None => {
// key does not exist, create it
suffixes = suffixes + (leading -> empty)
}
case _ =>
}
// recursion
suffixes(leading) withPrefix (s substring 1)
}
}
override def update(s: String, elem: T) = {
withPrefix(s).value = Some(elem)
}
override def remove(key: String): Option[T] = {
if (key.isEmpty) {
// base case. you are at the root
val prev = value
value = None
prev
} else {
// recursive
suffixes get key(0) flatMap (_.remove(key substring 1))
}
}
override def empty = PrefixMap.empty
}
import collection.mutable.{Builder, MapBuilder}
import collection.generic.CanBuildFrom
object PrefixMap {
def empty[T] = new PrefixMap[T]
def apply[T](kvs: (String, T)*): PrefixMap[T] = {
val m: PrefixMap[T] = empty
for(kv <- kvs) m += kv
m
}
def newBuilder[T]: Builder[(String, T), PrefixMap[T]] = {
new mutable.MapBuilder[String, T, PrefixMap[T]](empty)
}
implicit def canBuildFrom[T]: CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] = {
new CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] {
def apply(from: PrefixMap[_]) = newBuilder[T]
def apply() = newBuilder[T]
}
}
}
}
I don't understand this line:
def apply(from: PrefixMap[_]) = newBuilder[T]
What's point in receiving a PrefixMap and returning a empty PrefixMap?
Read little bit more official docs
If in short: CBF can return builder with knowledge of properties of whole collection.
For example it could preinitialize some buffer of needed size to collect entries.
Or even reuse some parts of collection of known type and structure.
But by default in many case it would just collect element by element to empty collection. That's happening in your case.
Here is an example from the stairway book:
object Example1 {
import collection._
class PrefixMap[T]
extends mutable.Map[String, T]
with mutable.MapLike[String, T, PrefixMap[T]] {
var suffixes: immutable.Map[Char, PrefixMap[T]] = Map.empty
var value: Option[T] = None
def get(s: String): Option[T] = {
// base case, you are at the root
if (s.isEmpty) value
// recursive
else suffixes get (s(0)) flatMap (_.get(s substring 1))
}
def withPrefix(s: String): PrefixMap[T] = {
if (s.isEmpty) this
else {
val leading = s(0)
suffixes get leading match {
case None => {
// key does not exist, create it
suffixes = suffixes + (leading -> empty)
}
case _ =>
}
// recursion
suffixes(leading) withPrefix (s substring 1)
}
}
override def update(s: String, elem: T) = {
withPrefix(s).value = Some(elem)
}
override def remove(key: String): Option[T] = {
if (key.isEmpty) {
// base case. you are at the root
val prev = value
value = None
prev
} else {
// recursive
suffixes get key(0) flatMap (_.remove(key substring 1))
}
}
def iterator: Iterator[(String, T)] = {
(for (v <- value.iterator) yield ("", v)) ++
(for ((chr, m) <- suffixes.iterator; (s, v) <- m.iterator) yield (chr +: s, v))
}
def +=(kv: (String, T)): this.type = {
update(kv._1, kv._2)
this
}
def -=(key: String): this.type = {
remove(key)
this
}
override def empty = new PrefixMap[T]
}
}
Note that for the methods += and -=, the return types are this.type. Can I use PrefixMap[T] here? If yes, what does this.type has to offer?
Can I use PrefixMap[T] here?
Yes.
If yes, what does this.type has to offer?
For example, imagine you also have another class PrefixMap2[T] extending PrefixMap. Then with this.type, += and -= called on a PrefixMap2 will return itself (and therefore, a PrefixMap2); with PrefixMap return type, the compiler will only know they return a PrefixMap.
I cannot find the way to define a MongoRecord with a Map[String,String] field inside it in Lift - MongoRecord.
The Lift documentation says:
All standard Record Fields are supported. There is also support for Mongo specific types; ObjectId, UUID, Pattern, List, and Map.
How can I define Map and List fields?
I defined a BsonRecordMapField:
class BsonRecordMapField[OwnerType <: BsonRecord[OwnerType], SubRecordType <: BsonRecord[SubRecordType]]
(rec: OwnerType, valueMeta: BsonMetaRecord[SubRecordType])(implicit mf: Manifest[SubRecordType])
extends MongoMapField[OwnerType, SubRecordType](rec: OwnerType) {
import scala.collection.JavaConversions._
override def asDBObject: DBObject = {
val javaMap = new HashMap[String, DBObject]()
for ((key, element) <- value) {
javaMap.put(key.asInstanceOf[String], element.asDBObject)
}
val dbl = new BasicDBObject(javaMap)
dbl
}
override def setFromDBObject(dbo: DBObject): Box[Map[String, SubRecordType]] = {
val mapResult: Map[String, SubRecordType] = (for ((key, dboEl) <- dbo.toMap.toSeq) yield (key.asInstanceOf[String], valueMeta.fromDBObject(dboEl.asInstanceOf[DBObject]))).toMap
setBox(Full(mapResult))
}
override def asJValue = {
val fieldList = (for ((key, elem) <- value) yield JField(key, elem.asJValue)).toList
JObject(fieldList)
}
override def setFromJValue(jvalue: JValue) = jvalue match {
case JNothing | JNull if optional_? => setBox(Empty)
case JObject(fieldList) => val retrievedMap = fieldList.map {
field =>
val key = field.name
val valRetrieved = valueMeta.fromJValue(field.value) openOr valueMeta.createRecord
(key, valRetrieved)
}.toMap
setBox(Full(retrievedMap))
case other => setBox(FieldHelpers.expectedA("JObject", other))
}
}
This is the implicit query for Rogue:
class BsonRecordMapQueryField[M <: BsonRecord[M], B <: BsonRecord[B]](val field: BsonRecordMapField[M, B])(implicit mf: Manifest[B]) {
def at(key: String): BsonRecordField[M, B] = {
val listBox = field.setFromJValue(JObject(List(JField("notExisting", JInt(0)))))
val rec = listBox.open_!.head._2
new BsonRecordField[M, B](field.owner, rec.meta)(mf) {
override def name = field.name + "." + key
}
}
}
object ExtendedRogue extends Rogue {
implicit def bsonRecordMapFieldToBsonRecordMapQueryField[M <: BsonRecord[M], B <: BsonRecord[B]](f: BsonRecordMapField[M, B])(implicit mf: Manifest[B]): BsonRecordMapQueryField[M, B] = new BsonRecordMapQueryField[M, B](f) (mf)
}
You can use the at operator in map now.
What about MongoMapField?
I'm using some Java code to do fast prefix lookups, using java.util.TreeSet, could I be using scala's TreeSet instead? Or a different solution?
/** A class that uses a TreeSet to do fast prefix matching
*/
class PrefixMatcher {
private val _set = new java.util.TreeSet[String]
def add(s: String) = _set.add(s)
def findMatches(prefix: String): List[String] = {
val matches = new ListBuffer[String]
val tailSet = _set.tailSet(prefix)
for ( tail <- tailSet.toArray ) {
val tailString = tail.asInstanceOf[String]
if ( tailString.startsWith(prefix) )
matches += tailString
else
return matches.toList
}
matches.toList
}
}
Use a Trie. Nobody's actually posted a Trie here yet, despite the fact that some people have posted sorted TreeMap data structures that they have misnamed as tries. Here is a fairly representative sample of a Trie implementation in Java. I don't know of any in Scala. See also an explanation of Tries on Wikipedia.
The from & takeWhile approach:
class PrefixMatcher {
private val _set = new TreeSet[String]
def add(s: String) = _set.add(s)
def findMatches(prefix: String): Iterable[String] =
_set from prefix takeWhile(_ startsWith prefix)
}
An alternative is to select a subset from prefix to prefix++ (the smallest string after the prefix). This selects only the range of the tree that actually starts with the given prefix. Filtering of entries is not necessary. The subSet method will create a view of the underlying set.
There's still some work (overflow and empty strings won't work) in the increment method but the intent should be clear.
class PrefixMatcher {
private val _set = new java.util.TreeSet[String]
def add(s: String) = _set.add(s)
def findMatches(prefix: String) : Set[String] = {
def inc(x : String) = { //ignores overflow
assert(x.length > 0)
val last = x.length - 1
(x take last) + (x(last) + 1).asInstanceOf[Char]
}
_set.subSet(prefix, inc(prefix))
}
}
The same works with the scala jcl.TreeSet#range implementation.
As I understand it, the Scala TreeSet is backed by the Java TreeSet, but using the Scala variant would allow you to shorten up the code using a sequence comprehension (http://www.scala-lang.org/node/111) giving you an implementation that looked something like (for Scala 2.7):
import scala.collection.jcl.TreeSet;
class PrefixMatcher
{
private val _set = new TreeSet[String]
def add(s: String) = _set.add(s)
def findMatches(prefix: String): Iterable[String] =
for (s <- _set.from(prefix) if s.startsWith(prefix)) yield s
}
object Main
{
def main(args: Array[String]): Unit =
{
val pm = new PrefixMatcher()
pm.add("fooBar")
pm.add("fooCow")
pm.add("barFoo")
pm.findMatches("foo").foreach(println)
}
}
Apologies for any bad Scala style on my part, I'm just getting used to the language myself.
I blogged about finding matches for a combination of prefixes a while ago. It's a harder problem, as you don't know when one prefix ends and the other begins. It might interest you. I'll even post below the code that I did not blog (yet, hopefully :), though it is stripped of all comments, none of which were made in English:
package com.blogspot.dcsobral.matcher.DFA
object DFA {
type Matched = List[(String, String)]
def words(s : String) = s.split("\\W").filter(! _.isEmpty).toList
}
import DFA._
import scala.runtime.RichString
class DFA {
private val initialState : State = new State(None, "")
private var currState : State = initialState
private var _input : RichString = ""
private var _badInput : RichString = ""
private var _accepted : Boolean = true
def accepted : Boolean = _accepted
def input : String = _input.reverse + _badInput.reverse
def transition(c : Char) : List[(String, Matched)] = {
if (c == '\b') backtrack
else {
if (accepted) {
val newState = currState(c)
newState match {
case Some(s) => _input = c + _input; currState = s
case None => _badInput = c + _badInput; _accepted = false
}
} else {
_badInput = c + _badInput
}
optionList
}
}
def transition(s : String) : List[(String, Matched)] = {
s foreach (c => transition(c))
optionList
}
def apply(c : Char) : List[(String, Matched)] = transition(c)
def apply(s : String) : List[(String,Matched)] = transition(s)
def backtrack : List[(String, Matched)] = {
if(_badInput isEmpty) {
_input = _input drop 1
currState.backtrack match {
case Some(s) => currState = s
case None =>
}
} else {
_badInput = _badInput drop 1
if (_badInput isEmpty) _accepted = true
}
optionList
}
def optionList : List[(String, Matched)] = if (accepted) currState.optionList else Nil
def possibleTransitions : Set[Char] = if (accepted) (currState possibleTransitions) else Set.empty
def reset : Unit = {
currState = initialState
_input = ""
_badInput = ""
_accepted = true
}
def addOption(s : String) : Unit = {
initialState addOption s
val saveInput = input
reset
transition(saveInput)
}
def removeOption(s : String) : Unit = {
initialState removeOption s
val saveInput = input
reset
transition(saveInput)
}
}
class State (val backtrack : Option[State],
val input : String) {
private var _options : List[PossibleMatch] = Nil
private val transitions : scala.collection.mutable.Map[Char, State] = scala.collection.mutable.Map.empty
private var _possibleTransitions : Set[Char] = Set.empty
private def computePossibleTransitions = {
if (! options.isEmpty)
_possibleTransitions = options map (_.possibleTransitions) reduceLeft (_++_)
else
_possibleTransitions = Set.empty
}
private def computeTransition(c : Char) : State = {
val newState = new State(Some(this), input + c)
options foreach (o => if (o.possibleTransitions contains c) (o computeTransition (newState, c)))
newState
}
def options : List[PossibleMatch] = _options
def optionList : List[(String, Matched)] = options map (pm => (pm.option, pm.bestMatch))
def possibleTransitions : Set[Char] = _possibleTransitions
def transition(c : Char) : Option[State] = {
val t = c.toLowerCase
if (possibleTransitions contains t) Some(transitions getOrElseUpdate (t, computeTransition(t))) else None
}
def apply(c : Char) : Option[State] = transition(c)
def addOption(option : String) : Unit = {
val w = words(option)
addOption(option, w.size, List(("", w.head)), w)
}
def addOption(option : String, priority : Int, matched : Matched, remaining : List[String]) : Unit = {
options find (_.option == option) match {
case Some(pM) =>
if (!pM.hasMatchOption(matched)) {
pM.addMatchOption(priority, matched, remaining)
if (priority < pM.priority) {
val (before, _ :: after) = options span (_ != pM)
val (highPriority, lowPriority) = before span (p => p.priority < priority ||
(p.priority == priority && p.option < option))
_options = highPriority ::: (pM :: lowPriority) ::: after
}
transitions foreach (t => pM computeTransition (t._2, t._1))
}
case None =>
val (highPriority, lowPriority) = options span (p => p.priority < priority ||
(p.priority == priority && p.option < option))
val newPM = new PossibleMatch(option, priority, matched, remaining)
_options = highPriority ::: (newPM :: lowPriority)
transitions foreach (t => newPM computeTransition (t._2, t._1))
}
computePossibleTransitions
}
def removeOption(option : String) : Unit = {
options find (_.option == option) match {
case Some(possibleMatch) =>
val (before, _ :: after) = options span (_ != possibleMatch)
(possibleMatch.possibleTransitions ** Set(transitions.keys.toList : _*)).toList foreach (t =>
transition(t).get.removeOption(option))
_options = before ::: after
computePossibleTransitions
case None =>
}
}
}
class PossibleMatch (val option : String,
thisPriority : Int,
matched : Matched,
remaining : List[String]) {
private var _priority = thisPriority
private var matchOptions = List(new MatchOption(priority, matched, remaining))
private var _possibleTransitions = matchOptions map (_.possibleTransitions) reduceLeft (_++_)
private def computePossibleTransitions = {
_possibleTransitions = matchOptions map (_.possibleTransitions) reduceLeft (_++_)
}
def priority : Int = _priority
def hasMatchOption(matched : Matched) : Boolean = matchOptions exists (_.matched == matched)
def addMatchOption(priority : Int, matched : Matched, remaining : List[String]) : Unit = {
if (priority < _priority) _priority = priority
val (highPriority, lowPriority) = matchOptions span (_.priority < priority)
val newMO = new MatchOption(priority, matched, remaining)
matchOptions = highPriority ::: (newMO :: lowPriority)
computePossibleTransitions
}
def bestMatch : Matched = matchOptions.head.matched.reverse.map(p => (p._1.reverse.toString, p._2)) :::
remaining.tail.map(w => ("", w))
def possibleTransitions : Set[Char] = _possibleTransitions
def computeTransition(s: State, c : Char) : Unit = {
def computeOptions(state : State,
c : Char,
priority : Int,
matched : Matched,
remaining : List[String]) : Unit = {
remaining match {
case w :: ws =>
if (!w.isEmpty && w(0).toLowerCase == c.toLowerCase) {
val newMatched = (w(0) + matched.head._1, matched.head._2.substring(1)) :: matched.tail
val newPriority = if (matched.head._1 isEmpty) (priority - 1) else priority
if (w.drop(1) isEmpty)
s.addOption(option, newPriority - 1, ("", ws.head) :: newMatched , ws)
else
s.addOption(option, newPriority, newMatched, w.substring(1) :: ws)
}
if (ws != Nil) computeOptions(s, c, priority, ("", ws.head) :: matched, ws)
case Nil =>
}
}
if(possibleTransitions contains c)
matchOptions foreach (mO => computeOptions(s, c, mO.priority, mO.matched, mO.remaining))
}
}
class MatchOption (val priority : Int,
val matched : Matched,
val remaining : List[String]) {
lazy val possibleTransitions : Set[Char] = Set( remaining map (_(0) toLowerCase) : _* )
}
It really needs some refactoring, though. I always do it when I'm start to explain it for the blog.
Ok, I just realized what you want is pretty much what a friend of mine suggested for another problem. So, here is his answer, simplified for your needs.
class PrefixMatcher {
// import scala.collection.Set // Scala 2.7 needs this -- and returns a gimped Set
private var set = new scala.collection.immutable.TreeSet[String]()
private def succ(s : String) = s.take(s.length - 1) + ((s.charAt(s.length - 1) + 1)).toChar
def add(s: String) = set += s
def findMatches(prefix: String): Set[String] =
if (prefix.isEmpty) set else set.range(prefix, succ(prefix))
}