Ответ 1
Я думаю, что вы ищете 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))
Как разбить список элементов в списки с не более чем N элементами?
ex: заданный список из 7 элементов, создайте группы из 4, оставив последнюю группу, возможно, с меньшим количеством элементов.
split(List(1,2,3,4,5,6,"seven"),4)
=> List(List(1,2,3,4), List(5,6,"seven"))
Я думаю, что вы ищете 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))
edit: после рассмотрения этого 2 года спустя я бы не рекомендовал эту реализацию, так как 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)
}
Я добавляю хвостовую рекурсивную версию метода 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)
}
Существует намного более простой способ выполнить задачу с помощью метода скольжения. Он работает следующим образом:
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))
Я думаю, что это реализация с использованием 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)