我可以使用折叠功能来实现包功能吗?

时间:2014-07-14 22:17:05

标签: scala functional-programming

我正在解决以下问题:

  

将列表元素的连续副本打包到子列表中。如果是一个清单   包含重复的元素,它们应放在单独的子列表中。   例如:

     

阶> pack(列表('a,'a,'a,'a,'b,'c,'c,'a,'a,'d,'e,'e,'e,   'e))res0:List [List [Symbol]] = List(List('a,'a,'a,'a),List('b),   列表('c,'c),列表('a,'a),列表('d),列表('e,'e,'e,'e))

我想知道是否可以使用foldRight实现它。到目前为止,我只能像下面这样做一个递归解决方案:

def pack(list: List[Char]) = {
  def checkNext(a: List[List[Char]], prev: Char, l: List[Char]): List[List[Char]] = l match {
    case Nil => a
    case h::tail if h == prev => checkNext((h::a.head)::a.tail,h,tail)
    case h::tail => checkNext(List(h)::a,h,tail)
  }
  checkNext(List(List[Char](list.last)), list.last, list.init.reverse) 
}

3 个答案:

答案 0 :(得分:2)

绝对!我发现使用fold来累积迭代序列的复杂结果是很自然的。从本质上讲,它与您现在正在执行的操作相同,只是列表上的匹配由fold提供给您,您只需提供案例处理。我不确定你是否想要一个真正的答案,所以我会尝试给你一些提示。

考虑最终结果的类型。现在想想将该过程应用于空序列的结果是该类型的值。这是您foldRight / foldLeft的第一个参数。

现在,您必须定义如何为您处理的每个项目扩展累加器。在我看来,你有两种情况:要么你曾经遇到过你以前没见过的新信,要么就是你要在现有清单中添加另一个实例。您可以使用一些花哨的匹配来检测您所处的情况。

以下是我的表现:

def pack(list: List[Char]) = list.foldLeft(List.empty[List[Char]]) { case (acc, next) =>
  acc.headOption.flatMap(_.headOption) match {
    case Some(x) if x == next => (acc.head :+ next) +: acc.tail
    case _ => List(next) +: acc
  }
}.reverse

我使用flatMap加入两个检查,以确定是否还有一个列表以及当前字符的列表是否存在。我发现foldLeft更加直观,并且还具有在List上进行尾递归的额外好处。

结果:

  

阶>打包(列出(' a',' a',' a',' a',' b',& #39; c',' c',' a',' a',' d',   ' e',' e' e'' e')

     

res1:List [List [Char]] = List(List(a,a,a,a),   列表(b),列表(c,c),列表(a,a),列表(d),列表(e,e,e,e))

答案 1 :(得分:2)

这是我的fold版本:

def pack[A](xs: List[A]): List[List[A]] =
  xs.foldRight(List[List[A]]()){
    case (x, (ys@(y::_)) :: rs) if x == y => (x::ys) :: rs
    case (x, ys) => List(x) :: ys
  }

但是,我更喜欢递归的那个:

def pack2[A](xs: List[A]): List[List[A]] = xs match {
  case Nil => Nil
  case x::_ => val (hs, ts) = xs.span(x==); hs::pack2(ts)
}

递归的更清晰,更短而不是fold版本,此外它更快

scala> def time(n: Int)(call : => Unit): Long = {
     |   var cnt = 0
     |   val start = System.currentTimeMillis
     |   while(cnt < n) {
     |     cnt += 1
     |     call
     |   }
     |   System.currentTimeMillis - start
     | }
time: (n: Int)(call: => Unit)Long

scala> val xs = ("A"*100 + "B"*1000 + "C"*10 + "DEFGH"*1000).toList
xs: List[Char] = List(A, A, A...)

scala> time(10000){ pack(xs) }
res3: Long = 19961

scala> time(10000){ pack2(xs) }
res4: Long = 4382

并将@ acjay的版本命名为pack3

scala> def pack3(list: List[Char]) = list.foldLeft(List.empty[List[Char]]) { case (acc, next) =>
     |   acc.headOption.flatMap(_.headOption) match {
     |     case Some(x) if x == next => (acc.head :+ next) +: acc.tail
     |     case _ => List(next) +: acc
     |   }
     | }.reverse
pack3: (list: List[Char])List[List[Char]]

scala> time(10000){ pack3(xs) }
res5: Long = 420946

scala> pack3(xs) == pack2(xs)
res6: Boolean = true

scala> pack3(xs) == pack(xs)
res7: Boolean = true

答案 2 :(得分:0)

Martin Odersky的实现

def pack[T](xs: List[T]): List[List[T]] = xs match{
    case Nil => Nil
    case x :: xs1 =>
        val (first, rest) = xs span (y => y == x)
        first :: pack(rest)
}
相关问题