2

我看到 Scala 标准库错过了获取集合中满足谓词的对象范围的方法:

def <???>(p: A => Boolean): List[List[A]] = {
  val buf = collection.mutable.ListBuffer[List[A]]()
  var elems = this.dropWhile(e => !p(e))
  while (elems.nonEmpty) {
    buf += elems.takeWhile(p)
    elems = elems.dropWhile(e => !p(e))
  }
  buf.toList
}

这种方法的好名字是什么?我的实施是否足够好?

4

4 回答 4

7

我会选择chunkWithchunkBy

至于你的实现,我认为这需要递归!看看能不能填这个

@tailrec def chunkBy[A](l: List[A], acc: List[List[A]] = Nil)(p: A => Boolean): List[List[A]] = l match {
  case Nil => acc
  case l    =>
    val next = l dropWhile !p
    val (chunk, rest) = next span p
    chunkBy(rest, chunk :: acc)(p)
}

为什么递归?它更容易理解算法并且更有可能没有错误(鉴于没有 vars)。

否定谓词的语法 !p 是通过隐式转换实现的

implicit def PredicateW[A](p: A => Boolean) = new {
  def unary_! : A => Boolean = a => !p(a)
}

我通常保留它,因为它非常有用

于 2012-10-22T05:35:23.487 回答
2

怎么样:

def chunkBy[K](f: A => K): Map[K, List[List[A]]] = ...

类似于groupBy但将连续的块保持为块。使用它,你可以xs.chunkBy(p)(true)得到你想要的。

于 2012-10-22T05:30:22.270 回答
2

您可能想要调用它,splitWith因为split它或多或少是执行此操作的字符串操作,它类似于splitAt.

顺便说一句,这是一个非常紧凑的实现(虽然它做了很多不必要的工作,所以它不是一个很好的速度实现;你的很好):

def splitWith[A](xs: List[A])(p: A => Boolean) = {
  (xs zip xs.scanLeft(1){ (i,x) => if (p(x) == ((i&1)==1)) i+1 else i }.tail).
  filter(_._2 % 2 == 0).groupBy(_._2).toList.sortBy(_._1).map(_._2.map(_._1))
}
于 2012-10-22T06:05:11.043 回答
1

只是稍微细化了oxbow的代码,这样签名更轻

def chunkBy[A](xs: List[A])(p: A => Boolean): List[List[A]] = {
  @tailrec
  def recurse(todo: List[A], acc: List[List[A]]): List[List[A]] = todo match {
    case Nil => acc
    case _ =>
      val next = todo dropWhile (!p(_))
      val (chunk, rest) = next span p
      recurse(rest, acc ::: List(chunk))
  }
  recurse(xs, Nil)
}
于 2012-10-22T10:50:14.097 回答