แยกรายการออกเป็นหลายรายการโดยมีจำนวนองค์ประกอบคงที่


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

ฉันมีคำถามแปลก ๆ ในกรณีเดียวกันถ้าฉันแปลงข้อมูลเป็นลำดับฉันจะได้รับ Stream Object ทำไมถึงเป็นเช่นนั้น?
Rakshith

3
@Rakshith นั่นฟังดูเหมือนคำถามแยกต่างหาก Scala มี 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 ปีต่อมาฉันจะไม่แนะนำให้ใช้งานนี้เนื่องจาก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)
  }

4
xs splitAt nเป็นอีกทางเลือกหนึ่งของการผสมผสานxs take nและxs drop n
Kipton Barros

1
สิ่งนี้จะระเบิดสแต็คพิจารณาการใช้งานซ้ำ
Jed Wesley-Smith

@Kipton จริง แต่คุณต้องดึงผลลัพธ์ไปไว้ในช่องชั่วคราวดังนั้นจึงเพิ่มสองสามบรรทัดให้กับวิธีการ ฉันทำเกณฑ์มาตรฐานอย่างรวดเร็วและดูเหมือนว่าใช้splitAtแทนtake/ dropปรับปรุงประสิทธิภาพโดยเฉลี่ยประมาณ 4% ทั้งสองอย่างเร็วกว่า 700-1000% .grouped(n).toList!
Luigi Plinge

@ หลุยกิววววว คิดว่าทำไมgrouped-toListช้าจัง ฟังดูเหมือนบั๊ก
Kipton Barros

@Jed คุณพูดถูกในกรณีที่รุนแรง แต่การใช้งานของคุณขึ้นอยู่กับสิ่งที่คุณใช้ สำหรับกรณีการใช้งานของ OP (ถ้าgroupedไม่มี :)) ความเรียบง่ายเป็นปัจจัยที่ลบล้าง สำหรับไลบรารีมาตรฐานความเสถียรและประสิทธิภาพควรเหนือกว่าความสง่างาม แต่มีตัวอย่างมากมายทั้งในการเขียนโปรแกรมใน Scalaและไลบรารีมาตรฐานของการเรียกแบบเรียกซ้ำแบบปกติ (แทนที่จะเรียกซ้ำ) มันเป็นอาวุธมาตรฐานและสำคัญในกล่องเครื่องมือ FP
Luigi Plinge

4

ฉันกำลังเพิ่มวิธีการแบ่งเวอร์ชันหางซ้ำเนื่องจากมีการพูดคุยเกี่ยวกับการเรียกซ้ำหางกับการเรียกซ้ำ ฉันใช้คำอธิบายประกอบ 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

0

ฉันคิดว่านี่คือการใช้งานโดยใช้ 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.