I have been trying to write some code in Scala to read a file and break it into lines for the purpose of adding words to a tree structure. I have taken several different approaches to this, but all have been met with the same error:
java.lang.IndexOutOfBoundsException: -1
at scala.collection.immutable.Vector.checkRangeConvert(Vector.scala:140)
at scala.collection.immutable.Vector.apply(Vector.scala:130)
at WordTree.addWord(Main.scala:27)
at bot$$anonfun$main$2.apply(bot.scala:12)
at bot$$anonfun$main$2.apply(bot.scala:10)
at scala.collection.Iterator$class.foreach(Iterator.scala:772)
at scala.collection.Iterator$$anon$22.foreach(Iterator.scala:451)
at bot$.main(bot.scala:10)
at bot.main(bot.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:78)
at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:24)
at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:88)
at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:78)
at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:33)
at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:40)
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:56)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:80)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:89)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
I’ve tried:
val wordtree = new WordTree
val wordlist = Source.fromFile("/usr/share/dict/words").getLines
for { line <- wordlist
if (line.length > 1)
} wordtree.addWord(line)
// As well as
val wordtree = new WordTree
Source.fromFile("/usr/share/dict/words").mkString("").split("\n").foreach(
wordtree.addWord(_))
As well as a number of variations using the elements present in each.
Is this a bug with the collections library or is there something seriously wrong with my code, here?
I’m using Scala 2.9.2 on OS X.
EDIT:
Here’s the code for the WordTree and Node.
class Node(val letter: Char, var endsWord: Boolean, var connections: List[Node] = List()) {
def getLink(letter: Char): Node = {
for (connection <- connections) {
if (connection.letter == letter)
return connection
}
null.asInstanceOf[Node]
}
def addLink(letter: Char, endsWord: Boolean) =
connections = (new Node(letter, endsWord)) :: connections
def printable: String =
"(" + letter.toString() + "->" + connections.map(_.printable).mkString(", ") + ")"
}
class WordTree() {
val alphabet = "abcdefghijklmnopqrstuvwxyz"
var base = alphabet.map(c => new Node(c.asInstanceOf[Char], false))
def addWord(word: String, n: Node = null): Node = {
var node = n
var link = n
if (word.length == 0) return null.asInstanceOf[Node]
if (n == null) node = base(alphabet.indexOf(word(0)))
if (word.length == 1) {
node.endsWord = true
return node
}
link = node.getLink(word(1).asInstanceOf[Char])
if (link != null)
return addWord(word.slice(1, word.length), link)
node.addLink(word(1).asInstanceOf[Char], false)
link = node.getLink(word(1).asInstanceOf[Char])
addWord(word.slice(1, word.length), link)
}
def findWord(word: String, n: Node = null): Node = {
var node = n
var link = n
if (n == null) node = base(alphabet.indexOf(word(0)))
if (word.length == 1)
return if (node.endsWord) node else null.asInstanceOf[Node]
link = node.getLink(word(1).asInstanceOf[Char])
if (link != null) return findWord(word.slice(1, word.length), link)
return null.asInstanceOf[Node]
}
}
The
alphabetvariable is incomplete, given the words from the dictionary. The dictionary file contains words that start with uppercase letters. When you search forindexOf("A")it will return -1 because “A” does not exist inalphabet.You can resolve this by either adding uppercase letters to
alphabet, or using .toLowerCase before passing the word intoaddWord.