Listeyi sabit sayıda öğeye sahip birden çok listeye bölün


119

Bir öğe listesini en fazla N öğeli listelere nasıl bölebilirim?

örn: 7 öğeli bir liste verildiğinde, son grubu muhtemelen daha az öğeyle bırakarak 4'lü gruplar oluşturun.

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

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

Yanıtlar:


213

Sanırım arıyorsun grouped. Bir yineleyici döndürür, ancak sonucu bir listeye dönüştürebilirsiniz,

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 listelerinde her şey için bir şeyler vardır.
J Atkin

Garip bir sorum var. Aynı durum için veriyi bir diziye çevirirsem, bir Akış Nesnesi elde ederim. Neden?
Rakshith

3
@Rakshith Bu ayrı bir soru gibi geliyor. Scala, bir veri yapısı seçen gizemli bir cüceye sahiptir ve sizin için bir Akış seçmiştir. Bir Liste istiyorsanız, bir Liste talep etmelisiniz, ancak sadece cücenin yargısına da güvenebilirsiniz.
Ion Freeman

12

Kaydırma yöntemini kullanarak görevi yapmanın çok daha kolay yolu var. Şu şekilde çalışır:

val numbers = List(1, 2, 3, 4, 5, 6 ,7)

Listeyi 3 boyutlu daha küçük listelere bölmek istediğinizi varsayalım.

numbers.sliding(3, 3).toList

sana vereceğim

List(List(1, 2, 3), List(4, 5, 6), List(7))

9

Veya kendiniz yapmak istiyorsanız:

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

kullanı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 : bunu 2 yıl sonra gözden geçirdikten sonra, bu uygulamayı önermem çünkü sizeO (n) ve bu nedenle bu yöntem O (n ^ 2) 'dir, bu da yerleşik yöntemin neden büyük listeler için daha hızlı hale geldiğini açıklar. aşağıdaki yorumlarda belirtildiği gibi. Aşağıdaki gibi verimli bir şekilde uygulayabilirsiniz:

def split[A](xs: List[A], n: Int): List[List[A]] =
  if (xs.isEmpty) Nil 
  else (xs take n) :: split(xs drop n, n)

hatta (biraz) daha verimli kullanarak 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 nkombinasyona bir alternatiftir xs take nvexs drop n
Kipton Barros

1
bu, yığını patlatacak, özyinelemeli bir uygulama düşünün
Jed Wesley-Smith

@Kipton, doğru, ancak sonuçları geçici vals'e çıkarmanız gerekir, böylece bir yönteme birkaç satır ekler. Hızlı bir kıyaslama yaptım ve bunun splitAtyerine kullanıyor take/ dropperformansı ortalama% 4 civarında artırıyor gibi görünüyor ; her ikisi de% 700-1000 daha hızlı .grouped(n).toList!
Luigi Plinge

@Luigi, Vay canına. Neden grouped-toListbu kadar yavaş olduğuna dair herhangi bir fikrin var mı? Bu bir böcek gibi geliyor.
Kipton Barros

@Jed Aşırı durumlarda haklısınız, ancak uygulamanız onu ne için kullandığınıza bağlı. OP'nin kullanım durumu için (eğer groupedyoksa :)), basitlik baskın faktördür. Standart kitaplık için kararlılık ve performans zarafetten üstün olmalıdır. Ancak hem Scala'da Programlama'da hem de normal özyinelemeli (kuyruk özyinelemeli yerine) çağrıların standart kitaplıklarında birçok örnek vardır ; FP araç kutusundaki standart ve önemli bir silahtır.
Luigi Plinge

4

Kuyruk özyinelemeye karşı özyinelemeye karşı bazı tartışmalar olduğundan, bölme yönteminin bir kuyruk özyinelemeli versiyonunu ekliyorum. Tailrec açıklamasını, uygulamanın gerçekten tail-recusive olmaması durumunda derleyiciyi şikayet etmeye zorlamak için kullandım. Kuyruk özyinelemesinin kaputun altında bir döngüye dönüştüğüne ve yığın sonsuza kadar büyümeyeceğinden büyük bir liste için bile sorun yaratmayacağına inanıyorum.

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
Bu cevap, bazı açıklamalar eklenerek geliştirilebilir. Kabul edilen cevabın kanonik, bunu yapmanın amaçlanan yolu olduğu göz önüne alındığında, birisinin neden bu cevabı tercih ettiğini açıklamalısınız.
Jeffrey Bosboom

0

Sanırım bu, take / drop yerine splitAt kullanan bir uygulama

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.