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

List 将列表拆分为具有固定元素数的多个列表,list,scala,split,List,Scala,Split,如何将元素列表拆分为最多包含N个项目的列表 例如:给定一个包含7个元素的列表,创建4个元素的组,最后一个组可能包含较少的元素 split(List(1,2,3,4,5,6,"seven"),4) => List(List(1,2,3,4), List(5,6,"seven")) 我想您正在寻找分组的。它返回一个迭代器,但您可以将结果转换为列表 scala> List(1,2,3,4,5,6,"seven").grouped(4).toList res0: List[List[An

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

例如:给定一个包含7个元素的列表,创建4个元素的组,最后一个组可能包含较少的元素

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

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

我想您正在寻找
分组的
。它返回一个迭代器,但您可以将结果转换为列表

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)
}
编辑:在两年后回顾这一点时,我不建议使用此实现,因为
大小
是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)
def split[X](n:Int,xs:List[X]):List[List[X]]=

如果(xs.size我正在添加split方法的尾部递归版本,因为有一些关于尾部递归与递归的讨论。我使用了tailrec注释,以迫使编译器在实现不是尾部递归的情况下进行投诉。我相信尾部递归会变成一个隐藏的循环,因此不会引起问题LEM即使对于一个大的列表也是如此,因为堆栈不会无限增长

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))

<代码> XS SPLITAT N/COD>是组合<代码> XS采取N<代码>和<代码> XS下拉N< /代码>的替代方案。这将导致堆栈爆炸,考虑递归。implementation@Kipton,是的,但您需要将结果提取到临时VAL中,以便为方法添加几行。我做了一个快速基准测试,它似乎使用了
splitAt
取而代之的是采取
/
放弃
平均提高4%左右的性能;两者都比
快700-1000%.toList
!@Luigi,哇。有没有想过为什么
分组toList
这么慢?这听起来像个bug。@Jed在极端情况下你是对的,但你的实现取决于你使用它的目的。对于OP的用例(如果
分组
不存在:),简单性是压倒一切的因素。对于标准库,稳定性和性能应该压倒优雅。但是在Scala编程和普通递归(而不是尾部递归)的标准库中都有很多例子调用;它是FP工具箱中的一个标准且重要的武器。Scala列表对所有东西都有作用。我有一个奇怪的问题。同样的情况下,如果我将数据转换为序列,我会得到一个流对象。为什么呢?@Rakshith这听起来像是一个单独的问题。Scala有一个神秘的gnome,它选择了一个数据结构,并选择了一个流对你来说。如果你想要一个列表,你应该要求一个列表,但你也可以相信侏儒的判断。这个答案可以通过添加一些解释来改进。考虑到被接受的答案似乎是实现这一点的规范的、预期的方式,你应该解释为什么有人会喜欢这个答案。
List(List(1, 2, 3), List(4, 5, 6), List(7))