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

发布于 2024-12-04 21:35:08 字数 188 浏览 0 评论 0原文

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

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

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

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

How to split a List of elements into lists with at most N items?

ex: Given a list with 7 elements, create groups of 4, leaving the last group possibly with less elements.

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

锦上情书 2024-12-11 21:35:08

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

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

I think you're looking for grouped. It returns an iterator, but you can convert the result to a list,

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))
仙气飘飘 2024-12-11 21:35:08

使用滑动方法有更简单的方法来完成任务。
它的工作方式是这样的:

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

There is much easier way to do the task using sliding method.
It works this way:

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

Lets say you want to break the list into smaller lists of size 3.

numbers.sliding(3, 3).toList

will give you

List(List(1, 2, 3), List(4, 5, 6), List(7))
岁月静好 2024-12-11 21:35:08

或者,如果您想自己制作:

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

Or if you want to make your own:

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

Use:

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

edit: upon reviewing this 2 years later, I wouldn't recommend this implementation since size is O(n), and hence this method is O(n^2), which would explain why the built-in method becomes faster for large lists, as noted in comments below. You could implement efficiently as follows:

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

or even (slightly) more efficiently using 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)
  }
勿忘初心 2024-12-11 21:35:08

我添加了 split 方法的尾递归版本,因为有一些关于尾递归与递归的讨论。我使用 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)
}

I am adding a tail recursive version of the split method since there was some discussion of tail-recursion versus recursion. I have used the tailrec annotation to force the compiler to complain in case the implementation is not indeed tail-recusive. Tail-recursion I believe turns into a loop under the hood and thus will not cause problems even for a large list as the stack will not grow indefinitely.

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)
}
献世佛 2024-12-11 21:35:08

我认为这是使用 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)

I think this is the implementation using splitAt instead of 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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文