Разделить список на каждый элемент, удовлетворяющий предикату (Scala) - PullRequest
12 голосов
/ 03 сентября 2011

В текстовом файле у меня есть данные в виде:

1)
text
text
2)
more text
3)
even more text
more even text
even more text
...

Я прочитал его как список строк, используя следующее:

val input = io.Source.fromFile("filename.txt").getLines().toList

Я хочу разбить список на подсписки, начинающиеся с 1), 2) и т. Д.

Я придумал:

val subLists =
  input.foldRight( List(List[String]()) ) {
    (x, acc) =>
      if (x.matches("""[0-9]+\)""")) List() :: (x :: acc.head) :: acc.tail
      else (x :: acc.head) :: acc.tail
  }.tail

Может ли это быть достигнуто проще? Что было бы действительно хорошо, было бы, если бы был встроенный метод для разделения коллекции на каждый элемент, который удовлетворяет предикату (подсказка, подсказка, дизайнеры библиотеки:)).

1 Ответ

24 голосов
/ 03 сентября 2011

foldRight со сложным аргументом обычно указывает на то, что вы могли бы также написать это, используя рекурсию, и выделить его в свой собственный метод, пока вы в нем. Вот что я придумал. Во-первых, давайте обобщим к универсальному методу groupPrefix:

 /** Returns shortest possible list of lists xss such that
  *   - xss.flatten == xs
  *   - No sublist in xss contains an element matching p in its tail
  */
 def groupPrefix[T](xs: List[T])(p: T => Boolean): List[List[T]] = xs match {
   case List() => List()
   case x :: xs1 => 
     val (ys, zs) = xs1 span (!p(_))
     (x :: ys) :: groupPrefix(zs)(p)  
 }

Теперь вы получите результат, просто позвонив

 groupPrefix(input)(_ matches """\d+\)""")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...