在满足谓词的每个元素上拆分列表(Scala)

时间:2011-09-03 14:10:40

标签: list scala collections

在文本文件中,我有以下格式的数据:

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

这可以更简单地实现吗?如果有一个内置方法可以在满足谓词的每个元素上拆分集合(提示,提示,库设计者:),那将会是非常好的。

2 个答案:

答案 0 :(得分:24)

带有复杂参数的

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+\)""")

答案 1 :(得分:1)

我很荣幸在伟大的@MartinOdersky旁边添加一个答案!

从Scala 2.13开始,我们可以使用List.unfold

List.unfold(input) {
  case Nil =>
    None
  case x :: as =>
    as.span(!_.matches("""\d+\)""")) match {
      case (prefix, Nil) =>
        Some(x :: prefix, List.empty)
      case (prefix, suffix) =>
        Some(x :: prefix, suffix)
    }
}

代码在Scastie处运行。

相关问题