将列表拆分为具有固定数量元素的多个列表

如何将一个元素列表拆分为最多 N 个项目的列表?

Ex: 给定一个包含7个元素的列表,创建4个组,最后一个组可能包含更少的元素。

split(List(1,2,3,4,5,6,"seven"),4)


=> List(List(1,2,3,4), List(5,6,"seven"))
81061 次浏览

我想你要找的是 grouped。它返回一个迭代器,但是你可以把结果转换成一个列表,

scala> List(1,2,3,4,5,6,"seven").grouped(4).toList
res0: List[List[Any]] = List(List(1, 2, 3, 4), List(5, 6, seven))

或者如果你想自己做:

def split[A](xs: List[A], n: Int): List[List[A]] = {
if (xs.size <= n) xs :: Nil
else (xs take n) :: split(xs drop n, n)
}

用途:

scala> split(List(1,2,3,4,5,6,"seven"), 4)
res15: List[List[Any]] = List(List(1, 2, 3, 4), List(5, 6, seven))

编辑 : 在两年后回顾这个实现时,我不会推荐这个实现,因为 size是 O (n) ,因此这个方法是 O (n ^ 2) ,这就解释了为什么内置方法在大型列表中变得更快,如下面的注释所示。您可以按照以下方式有效地执行:

def split[A](xs: List[A], n: Int): List[List[A]] =
if (xs.isEmpty) Nil
else (xs take n) :: split(xs drop n, n)

甚至(略微)更有效地使用 splitAt:

def split[A](xs: List[A], n: Int): List[List[A]] =
if (xs.isEmpty) Nil
else {
val (ys, zs) = xs.splitAt(n)
ys :: split(zs, n)
}

我认为这是使用 splitAt 代替 take/drop 的实现

def split [X] (n:Int, xs:List[X]) : List[List[X]] =
if (xs.size <= n) xs :: Nil
else   (xs.splitAt(n)._1) :: split(n,xs.splitAt(n)._2)

我添加了一个分割方法的尾部递归版本,因为有一些关于尾部递归和递归的讨论。我使用了 tailrec 注释来强制编译器在实现实际上不是尾部递归的情况下进行抱怨。我相信尾递归会变成一个隐藏的循环,因此即使对于一个大的列表也不会造成问题,因为堆栈不会无限增长。

import scala.annotation.tailrec




object ListSplitter {


def split[A](xs: List[A], n: Int): List[List[A]] = {
@tailrec
def splitInner[A](res: List[List[A]], lst: List[A], n: Int) : List[List[A]] = {
if(lst.isEmpty) res
else {
val headList: List[A] = lst.take(n)
val tailList : List[A]= lst.drop(n)
splitInner(headList :: res, tailList, n)
}
}


splitInner(Nil, xs, n).reverse
}


}


object ListSplitterTest extends App {
val res = ListSplitter.split(List(1,2,3,4,5,6,7), 2)
println(res)
}

有更简单的方法来做这项任务使用滑动方法。 它是这样运作的:

val numbers = List(1, 2, 3, 4, 5, 6 ,7)

假设您想将该列表分解为大小为3的较小列表。

numbers.sliding(3, 3).toList

会给你

List(List(1, 2, 3), List(4, 5, 6), List(7))