要素のリストを最大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"))
回答:
あなたが探していると思います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))
編集:これを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)
}
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)
}