99 scala problems有这个问题:
将列表元素的连续副本打包到子列表中。 如果列表包含重复的元素,则应将它们放在单独的子列表中。
Example:
scala> pack(List('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), List('c, 'c), List('a, 'a), List('d), List('e, 'e, 'e, 'e))
我对解决上述问题的尾递归方法有所了解。我想知道是否有办法使用scanLeft完成上述操作,其中中间结果是常用元素列表?
答案 0 :(得分:1)
以下是使用foldLeft
的解决方案:
def pack[A](l:List[A]): List[List[A]] = l match {
case head :: tail =>
tail.foldLeft(List(List(head))) { (collector:List[List[A]], elem:A) =>
if (collector.head.head == elem)
(elem +: collector.head) +: collector.tail
else
List(elem) +: collector
}.reverse
case _ => List.empty
}
这仅适用于列表。
更好的解决方案可能会使用MultiSets
,尽管很难找到Scala实现。
答案 1 :(得分:1)
简洁但未经优化的版本可能如下所示:
val l = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)
for (i <-l.distinct) yield l.filter(_ == i)
res0: List[List[Symbol]] = List(List('a, 'a, 'a, 'a, 'a, 'a), List('b), List('c, 'c), List('d), List('e, 'e, 'e, 'e))
答案 2 :(得分:0)
scanLeft对于计算前缀和非常有用,但问题并非如此。您可以尝试将其与其他呼叫结合使用,但我认为这不是解决问题的好方法。
foldLeft似乎对此任务更有用。
def pack[T](list: Seq[T]) = list.foldLeft(new ArrayBuffer[ArrayBuffer[T]])((ret, el) => {
if (ret.isEmpty || ret.last.head != el)
ret.append(new ArrayBuffer[T])
ret.last.append(el)
ret
})
为好奇的读者留下了两个简单的任务:
答案 3 :(得分:0)
不,scanLeft
在这种情况下不是合适的方法。 scanLeft
将生成一个结果集合,其中包含列表中每个值的一个元素,以及您提供的初始值。因此,例如在scanLeft
上使用List(1,2,3,4)
将始终产生包含5个元素的结果
e.g。
scala> List(1,2,3,4).scanLeft(">"){case (last, x) => last + x}
res7: List[String] = List(>, >1, >12, >123, >1234)
答案 4 :(得分:0)
unfold
构建器现在提供了从Scala 2.13
开始的List
,该构建器可以与List::span
结合使用:
// val list = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)
List.unfold(list) {
case Nil => None
case rest => Some(rest.span(_ == rest.head))
}
// List[List[Symbol]] = List(List('a, 'a, 'a, 'a), List('b), List('c, 'c), List('a, 'a), List('d), List('e, 'e, 'e, 'e))
,或者与Scala 2.13
的{{3}}构建器一起使用:
List.unfold(list) {
rest => Option.unless(rest.isEmpty)(rest.span(_ == rest.head))
}
详细信息:
list
初始化的,以拆分List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)
span
处于该内部状态,以查找包含相同符号的前缀:l.span(_ == l.head)
,该符号在第一次迭代l.span(_ == 'a)
期间给出(List('a, 'a, 'a, 'a),List('b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e))
unfold
期望的那样,元组的Option
每次迭代的第一部分是要添加到正在构建的列表中的新元素(此处为List('a, 'a, 'a, 'a)
),第二部分是内部状态的新值(此处为List('b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)
),该值正好符合该要求。unfold
来告诉None
我们已经完成。