Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatic Scala List Comprehension - first item that matches

Folks, I've been writing some code in Scala lately to teach myself the language and in some recent experiments, I've used an NLP library to produce a set of part-of-speech tagged words from a user's input.

I want to write a function that gives me the first verb in the sentence. If there are no verbs, then I want to assume that the first word in the set is the verb (e.g. if the player just typed "who" or "uptime" , those are considered verbs by my game).

The following is a block of code so ugly only a mother could love, and it stinks of imperative programming and I want to refactor it into something more like idiomatic Scala, ideally something that doesn't have a single "if" statement in it.

def firstVerb = {
    if (words.size == 1)
        words.head.value
    else {
        val outWords = words.filter( word => word.pos == Verb)
        if (outWords == Set.empty) 
            words.head.value
        else
            outWords.head.value 
    }
}

The "words" variable is of type ListBuffer[EnrichedWord], where EnrichedWord is my class that contains a part of speech (pos, contains case objects like Verb, Noun, etc) and the original word (value).

Any guidance you Scala geniuses can provide in refactoring this butt-ugly code would be fantastic.

like image 821
Kevin Hoffman Avatar asked Feb 20 '12 14:02

Kevin Hoffman


2 Answers

This additionally handles the case when words is empty, try it:

words.find(_.pos == Verb).orElse(words.headOption).map(_.value).getOrElse("")

If you are sure words will never be an empty Set, this one is simpler:

words.find(_.pos == Verb).getOrElse(words.head).value

BTW if you are using HashSet the notion of some element being first doesn't really make sense. If each element represents a word in a sentece, it should have been a List or a Seq.

like image 157
Tomasz Nurkiewicz Avatar answered Nov 05 '22 07:11

Tomasz Nurkiewicz


The canonical (point-free) version is probably:

words find(_.pos == Verb) orElse words.headOption map _.value getOrElse ""

Another option is:

(words.find(_.pos == Verb) ++ words.take(1)).take(1).map(_.value).mkString
like image 24
Rex Kerr Avatar answered Nov 05 '22 06:11

Rex Kerr