web-dev-qa-db-ja.com

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

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

例:7つの要素を持つリストが与えられた場合、4つのグループを作成し、最後のグループの要素数を減らします。

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

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

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))
188
Kipton Barros

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

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

または、独自に作成する場合:

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

末尾再帰と再帰の議論があったので、分割メソッドの末尾再帰バージョンを追加しています。私は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)
}
3
Mike

これは、テイク/ドロップの代わりに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)
0
Hydrosan