如何将元素列表拆分为最多包含N个项目的列表?
例如:给定一个包含7个元素的列表,创建4个组,而最后一个组可能包含较少的元素。
split(List(1,2,3,4,5,6,"seven"),4)
=> List(List(1,2,3,4), List(5,6,"seven"))
如何将元素列表拆分为最多包含N个项目的列表?
例如:给定一个包含7个元素的列表,创建4个组,而最后一个组可能包含较少的元素。
split(List(1,2,3,4,5,6,"seven"),4)
=> List(List(1,2,3,4), List(5,6,"seven"))
Answers:
我认为您正在寻找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)
}
xs splitAt n
是该组合的替代方案,xs take n
以及xs drop n
splitAt
代替take
/将drop
性能平均提高了大约4%;两者都比700-1000%快.grouped(n).toList
!
grouped-toList
这么慢有什么想法吗?听起来像个虫子。
grouped
不存在:)),简单性是首要因素。对于标准库,稳定性和性能应胜过优雅。但是在Scala编程和普通递归(而不是尾递归)调用的标准库中都有很多示例。它是FP工具箱中的标准且重要的武器。
由于添加了尾递归与递归的讨论,因此我添加了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)
}