序列上的n-way`span`

时间:2013-01-10 16:17:55

标签: scala

给定一系列元素和一个谓词p,我想生成一系列序列,这样,在每个子序列中,要么所有元素都满足p,要么序列的长度为{{1 }}。另外,在结果上调用1会让我恢复原始序列(因此不会重新排序元素)。

例如,给定:

.flatten

我希望val l = List(2, 4, -6, 3, 1, 8, 7, 10, 0) val p = (i : Int) => i % 2 == 0 能够制作:

magic(l,p)

我知道List(List(2, 4, -6), List(3), List(1), List(8), List(7), List(10, 0)) ,但该方法在第一次遇到不满足.span的值并且只返回一对时停止。

以下是候选实施方案。它做我想要的,但是,好吧,让我们想哭。我希望有人能够提出一些更具惯用性的东西。

p

(请注意,我并不特别关心保留def magic[T](elems : Seq[T], p : T=>Boolean) : Seq[Seq[T]] = { val loop = elems.foldLeft[(Boolean,Seq[Seq[T]])]((false,Seq.empty)) { (pr,e) => val (lastOK,s) = pr if(lastOK && p(e)) { (true, s.init :+ (s.last :+ e)) } else { (p(e), s :+ Seq(e)) } } loop._2 } 的实际类型。)

3 个答案:

答案 0 :(得分:3)

对于此任务,您可以使用takeWhiledrop结合匹配递归的小模式:

def magic[T](elems : Seq[T], p : T=>Boolean) : Seq[Seq[T]] = {
  def magic(elems: Seq[T], result: Seq[Seq[T]]): Seq[Seq[T]] = elems.takeWhile(p) match {
    // if elems is Nil, we have a result
    case Nil if elems.isEmpty => result

    // if it's not, but we don't get any values from takeWhile, we take a single elem
    case Nil => magic(elems.tail, result :+ Seq(elems.head))

    // takeWhile gave us something, so we add it to the result
    // and drop as many elements from elems, as takeWhile gave us
    case xs => magic(elems.drop(xs.size), result :+ xs)
  }

  magic(elems, Seq())
}

答案 1 :(得分:3)

我不会使用foldLeft。如果头部与谓词不匹配,它只是span的简单递归和特殊规则:

def magic[T](elems: Seq[T], p: T => Boolean): Seq[Seq[T]] = 
  elems match {
    case Seq() => Seq()
    case Seq(head, tail @ _*) if !p(head) => Seq(head) +: magic(tail, p)
    case xs => 
      val (prefix, rest) = xs span p
      prefix +: magic(rest, p)
  }

你也可以进行尾递归,但是如果你在前置(合理的话),你需要记住反转输出:

def magic[T](elems: Seq[T], p: T => Boolean): Seq[Seq[T]] = {
  def iter(elems: Seq[T], out: Seq[Seq[T]]) : Seq[Seq[T]] = 
    elems match {
      case Seq() => out.reverse
      case Seq(head, tail @ _*) if !p(head) => iter(tail, Seq(head) +: out)
      case xs => 
        val (prefix, rest) = xs span p
        iter(rest, prefix +: out)
    }
  iter(elems, Seq())
}

答案 2 :(得分:0)

另一种使用折叠的解决方案:

def magicFilter[T](seq: Seq[T], p: T => Boolean): Seq[Seq[T]] = {
  val (filtered, current) = (seq foldLeft (Seq[Seq[T]](), Seq[T]())) {
    case ((filtered, current), element) if p(element)       => (filtered, current :+ element)
    case ((filtered, current), element) if !current.isEmpty => (filtered :+ current :+ Seq(element), Seq())
    case ((filtered, current), element)                     => (filtered :+ Seq(element), Seq())
  }
  if (!current.isEmpty) filtered :+ current else filtered
}