Home > Enterprise >  Functional (Scala) programming: Split a List[Char] on a token
Functional (Scala) programming: Split a List[Char] on a token

Time:04-09

What is the general accepted implementation of a recursive split-by-token function?

For example:

val s: List[Char] = 'F' :: 'o' :: 'o' :: ' ' :: 'b' :: 'a' :: 'r' :: ' ' :: 'f' :: 'o':: 'o':: ' ' :: 'b' :: 'a' :: 'r' :: Nil
fooSplit(s) // -> List(List('F','o','o'), List('b', 'a', 'r'), List('f', 'o', 'o'), List('b','a','r'))

Here are the base cases I've come up with, when doing pattern matching on the parameter:

def fooSplit(text: List[Char]): List[List[Char]] = text match {
  case x :: ' ' :: xs =>
    // Somehow append old list to fooSplit(xs) <?>
    println(x   "_")
    fooSplit(xs)

  case x :: xs =>
    println(x   "")
    fooSplit(xs)

  case Nil => Nil
}

Most of my attempts following the above structure almost always lead to List('F', List('O', List('O'), ...)...). Without coming up with some very sinister double pattern matching I can't seem to wrap my head on to extend instead of append to the inside list.


I would also like to quote the Scala Cookbook:

Use flatMap in situations where you run map followed by flatten. The specific situation is this:

• You’re using map (or a for/yield expression) to create a new collection from an existing collection.
The resulting collection is a list of lists.
• You call flatten immediately after map (or a for/yield expression)

This seems like a very possible case to use flatMap, could anyone provide insight on this?

CodePudding user response:

Just because you're returning an instance of List[List[String]] doesn't mean you need to flatten it, sometimes you might need those values. I would suggest this:

scala> import scala.annotation.tailrec
import scala.annotation.tailrec

scala> def fooSplit(text: List[Char]): List[List[Char]] = {
     |   
     |   @tailrec
     |   def fooSplitHelper(currentListBeforeToken: List[Char] = List.empty, list: List[Char], acc: List[List[Char]] = List.empty): List[List[Char]] = {
     |     if (list.isEmpty) {
     |       acc :  currentListBeforeToken
     |     } else {
     |       list.head match {
     |         case ' ' => fooSplitHelper(List.empty[Char], list.tail, acc :  currentListBeforeToken)
     |         case otherChar => fooSplitHelper(currentListBeforeToken :  otherChar, list.tail, acc)
     |       }
     |     }
     |   }
     |   
     |   fooSplitHelper(list = text)
     | }
def fooSplit(text: List[Char]): List[List[Char]]
                                                                                
scala> fooSplit(s)
val res0: List[List[Char]] = List(List(F, o, o), List(b, a, r), List(f, o, o), List(b, a, r))
  • Related