要素数が固定されたリストを複数のリストに分割する


119

要素のリストを最大N項目のリストに分割する方法は?

例:7つの要素を持つリストが与えられた場合、4つのグループを作成し、最後のグループは要素が少ない可能性があります。

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

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

回答:


213

あなたが探していると思います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))

25
Scalaのリストには、すべてに何かがある。
J Atkin

奇妙な質問があります。同じケースで、データをシーケンスに変換すると、ストリームオブジェクトが取得されます。何故ですか?
Rakshith、2016年

2
@Rakshithそれは別の質問のように聞こえます。Scalaには、データ構造を選択する神秘的なgnomeがあり、ストリームを選択します。リストが必要な場合は、リストをリクエストする必要がありますが、gnomeの判断を信頼することもできます。
Ion Freeman

12

スライド方式を使用してタスクを実行するはるかに簡単な方法があります。それはこのように機能します:

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

9

または、自分で作成したい場合:

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年後にレビューすると、sizeO(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)
  }

4
xs splitAt n組み合わせの代替手段xs take nであるxs drop n
Kipton Barros '18

1
これはスタックを爆発させます。再帰的な実装を検討してください
Jed Wesley-Smith

@ Kipton、true、ただし、結果を一時的なvalに抽出して、メソッドに数行追加する必要があります。私は簡単なベンチマークを実行しsplitAtましたが、take/の代わりに使用するとdrop、平均で約4%パフォーマンスが向上します。どちらも700-1000%速いです.grouped(n).toList
Luigi Plinge

@ルイジ、すごい。なぜgrouped-toListそんなに遅いのかについての考えはありますか?それはバグのように聞こえます。
Kiptonバロス

@Jed極端な場合は正しいですが、実装はそれを何に使用するかによって異なります。OPのユースケース(grouped存在しない場合:))の場合、単純さが最優先されます。標準ライブラリの場合、安定性とパフォーマンスが優雅さを上回ります。しかし、「Scalaでのプログラミング」と、(末尾再帰ではなく)通常再帰呼び出しの標準ライブラリーの両方に多くの例があります。これは、FPツールボックスの標準的かつ重要な武器です。
Luigi Plinge

4

末尾再帰と再帰の議論があったため、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)
}

1
この答えは、いくつかの説明を追加することで改善できます。受け入れられた回答がこれを行うための標準的な意図された方法であると思われる場合、誰かがこの回答を好む理由を説明する必要があります。
Jeffrey Bosboom 2016

0

これはテイク/ドロップの代わりにsplitAtを使用した実装だと思います

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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.