วิธีแบ่งรายการองค์ประกอบออกเป็นรายการโดยมีรายการมากที่สุด 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))
มีวิธีที่ง่ายกว่ามากในการทำงานโดยใช้วิธีการเลื่อน มันทำงานในลักษณะนี้:
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))
หรือหากคุณต้องการทำของคุณเอง:
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
ฉันกำลังเพิ่มวิธีการแบ่งเวอร์ชันหางซ้ำเนื่องจากมีการพูดคุยเกี่ยวกับการเรียกซ้ำหางกับการเรียกซ้ำ ฉันใช้คำอธิบายประกอบ 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)
}
ฉันคิดว่านี่คือการใช้งานโดยใช้ 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)