I just had a use case where I needed to split a list into n sublists, so that elements are taken in order from the original list and grouped while the predicate is true for the sublist (when it is false, a new sublist is started). I didn’t find this functionality in the standard library, and I thought it was a good exercise to try to solve it in a functional style (as I’m far from a functional guru).
Below is the code I came up with. But I suspect it can be improved a lot. Can you help me find a better way to code this?
class ListWithSplitter[A](val theList:List[A]) { private def sublistWhile(list:List[A], pred:(List[A] => Boolean)):(List[A],List[A]) = { def combine(okList:List[A], remaining:List[A], pred:(List[A] => Boolean)):(List[A],List[A]) = { if(pred(okList ::: remaining.head :: Nil)) combine(okList ::: remaining.head :: Nil, remaining.tail, pred) else (okList, remaining) } list match { case Nil => (Nil, Nil) case x :: Nil => (list, Nil) case x :: xs => combine(List(x), xs, pred) } } private def combinedSplit(list:List[A], pred:(List[A] => Boolean)):List[List[A]] = { val r = sublistWhile(list, pred) r match { case (Nil, Nil) => List(Nil) case (x, Nil) => List(x) case (x, y) => x :: combinedSplit(y, pred) } } def combinedSplit(pred:(List[A] => Boolean)):List[List[A]] = { combinedSplit(theList, pred) } } trait ListCombinedSplit { implicit def list2combSplitter[A](x:List[A]) : ListWithSplitter[A] = new ListWithSplitter(x) } object ListSplitter extends ListCombinedSplit { def main(args:Array[String]) { // sample usage: sum of each sublist is less than 100 val a = List(4, 59, 10, 24, 42, 9, 2, 44, 44, 44, 44) val b = a combinedSplit { list:List[Int] => ((0 /: list)(_ + _)) < 100 } b foreach println } }
Result of sample is:
List(4, 59, 10, 24) List(42, 9, 2, 44) List(44, 44) List(44)
Your code has the problem that mere invocations of your predicate make it O(N^2). Also, I think the object oriented stuff is too complicated.
I came up with:
This doesn’t allow you to specify arbitrary predicate, but you can make it work for fold-like predicates by changing manipulation with the first elent of the pair in foldLeft.