I recently came across this post, where Joey Gibson presents two implementations of a function to find the longest word in an array of strings. The procedural implementation, which often is what you start with when converting from Java to Scala, looks like this:
def longestWord(words: Array[String]) = {
var word = words(0)
var idx = 0
for (i <- 1 until words.length)
if (words(i).length > word.length) {
word = words(i)
idx = i
}
(word, idx)
}
It is a bit nicer than a corresponding implementation in Java but doesn't buy you much. The functional version Joey presented, is much shorter:
def longestWord(words: Array[String]) =
(("", -1) /: words.zipWithIndex) {(old, cur) =>
if (cur._1.length > old._1.length) cur
else old
}
}
It always gives me a kick to see how much shorter and more readable functional/Scala code is in comparison to procedural/Java code. In this case, however, readers rightfully argued that this isn't very readable. But luckily we can make it readable and even shorter! Firstly, instead of using a fold left (/:), we could use a reduceLeft
def longestWord(words: Iterable[String]) = {
words.zipWithIndex.reduceLeft{(a,b) =>
if (a._1.length > b._1.length) a else b}
}
}
which already is an improvement in readability. Note that I also changed the parameter type from Array[String] to Iterable[String]. This way the function works for lists, arrays, ..., anything Iterable.
Now I am getting greedy. I am not sure if the maxBy function was around in 2010 when Joey wrote his post, but these days we can simply write
def longestWord(words: Iterable[String]) =
words.zipWithIndex.maxBy(_._1.length)
which is super short and very readable. If you don't like the tuple notation then this version is a bit more explicit and the most readable, I believe:
def longestWord(words: Iterable[String]) =
words.zipWithIndex.maxBy{case (word,_) => word.length}
Of course, if you only want the longest word (and not its index) words.maxBy(_.length) will do the job.
No comments:
Post a Comment