我正在解决以下问题:
将列表元素的连续副本打包到子列表中。如果是一个清单 包含重复的元素,它们应放在单独的子列表中。 例如:
阶> 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)
}
答案 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)
}