วิธีที่ต้องการในการสร้างรายการ Scala


117

มีหลายวิธีในการสร้างรายการที่ไม่เปลี่ยนรูปใน Scala (ดูโค้ดตัวอย่างที่สร้างไว้ด้านล่าง) คุณสามารถใช้ ListBuffer ที่ไม่เปลี่ยนแปลงสร้างvarรายการและแก้ไขได้ใช้วิธีการเรียกซ้ำหางและอาจเป็นวิธีอื่นที่ฉันไม่รู้

โดยสัญชาตญาณฉันใช้ ListBuffer แต่ฉันไม่มีเหตุผลที่ดีในการทำเช่นนั้น มีวิธีการที่ต้องการหรือเป็นสำนวนในการสร้างรายการหรือมีสถานการณ์ที่ดีที่สุดสำหรับวิธีการหนึ่งมากกว่าอีกวิธีหนึ่งหรือไม่?

import scala.collection.mutable.ListBuffer

// THESE are all the same as: 0 to 3 toList.
def listTestA() ={
    var list:List[Int] = Nil

    for(i <- 0 to 3) 
        list = list ::: List(i)
    list
}


def listTestB() ={
    val list = new ListBuffer[Int]()

    for (i <- 0 to 3) 
        list += i
    list.toList
}


def listTestC() ={
    def _add(l:List[Int], i:Int):List[Int] = i match {
        case 3 => l ::: List(3)
        case _ => _add(l ::: List(i), i +1)
    }
    _add(Nil, 0)
}

คำตอบ:


108

ListBufferเป็นรายการที่ไม่แน่นอนซึ่งมีการผนวกเวลาคงที่และการแปลงเวลาคงที่เป็นไฟล์List.

List ไม่เปลี่ยนรูปและมีค่าคงที่เวลานำหน้าและต่อท้ายเวลาเชิงเส้น

วิธีสร้างรายการของคุณขึ้นอยู่กับอัลกอริทึมที่คุณจะใช้รายการและลำดับที่คุณได้รับองค์ประกอบในการสร้าง

ตัวอย่างเช่นหากคุณได้รับองค์ประกอบในลำดับตรงข้ามกับเวลาที่จะใช้คุณก็สามารถใช้ a Listและทำ prepends ไม่ว่าคุณจะทำโดยใช้ฟังก์ชัน tail-recursive foldLeftหรืออย่างอื่นก็ไม่เกี่ยวข้องจริงๆ

หากคุณได้รับองค์ประกอบตามลำดับเดียวกับที่คุณใช้องค์ประกอบนั้นListBufferน่าจะเป็นทางเลือกที่ดีกว่าหากประสิทธิภาพเป็นสิ่งสำคัญ

แต่ถ้าคุณไม่ได้อยู่ในเส้นทางวิกฤตและอินพุตต่ำพอคุณสามารถreverseแสดงรายการในภายหลังได้ตลอดเวลาหรือเพียงแค่foldRightหรือreverseอินพุตซึ่งเป็นเวลาเชิงเส้น

สิ่งที่คุณไม่ควรทำคือใช้ a Listและต่อท้าย สิ่งนี้จะให้ประสิทธิภาพที่แย่กว่าการเติมเงินล่วงหน้าและการย้อนกลับในตอนท้าย


What you DON'T do is use a List and append to itนั่นเป็นเพราะมีการสร้างรายการใหม่หรือไม่? ในขณะที่การใช้การดำเนินการเพิ่มจะไม่สร้างรายการใหม่?
Kevin Meredith

2
@KevinMeredith ใช่. ผนวกคือ O (n) นำหน้าคือ O (1)
Daniel C. Sobral

@pgoggijr นั่นไม่เป็นความจริง ประการแรกไม่มีการ "เปลี่ยนแปลง" ที่ใดเพราะไม่เปลี่ยนรูป Nilสำรวจเส้นทางถูกต้องเนื่องจากองค์ประกอบทั้งหมดจะต้องมีการคัดลอกเพียงเพื่อให้สำเนาขององค์ประกอบที่ผ่านมาสามารถทำชี้ไปที่องค์ประกอบใหม่แทน ประการที่สองไม่มีสำเนาใด ๆ ที่อยู่ข้างหน้า: องค์ประกอบถูกสร้างขึ้นโดยชี้ไปที่รายการที่มีอยู่และนั่นก็เท่านั้น
Daniel C. Sobral


22

อืม .. มันดูซับซ้อนเกินไปสำหรับฉัน ขอเสนอ

def listTestD = (0 to 3).toList

หรือ

def listTestE = for (i <- (0 to 3).toList) yield i

ขอบคุณสำหรับคำตอบ แต่คำถามคือคุณจะทำอย่างไรในกรณีที่ไม่สำคัญ ฉันใส่ความคิดเห็นในรหัสอธิบายว่าพวกเขาทั้งหมดเทียบเท่ากับ 0 ถึง 3 toList
agilefall

อ๊ะขอโทษแล้ว! ตรงไปตรงมาฉันไม่เคยใช้ ListBuffer
Alexander Azarov

5

คุณต้องการเน้นที่ความไม่เปลี่ยนรูปใน Scala โดยทั่วไปโดยการกำจัด vars ใด ๆ ความสามารถในการอ่านยังคงเป็นสิ่งสำคัญสำหรับเพื่อนมนุษย์ของคุณดังนั้น:

ลอง:

scala> val list = for(i <- 1 to 10) yield i
list: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

คุณอาจไม่จำเป็นต้องแปลงเป็นรายการในกรณีส่วนใหญ่ :)

seq ที่จัดทำดัชนีจะมีทุกสิ่งที่คุณต้องการ:

นั่นคือตอนนี้คุณสามารถทำงานกับ IndexedSeq นั้นได้:

scala> list.foldLeft(0)(_+_)
res0: Int = 55

Vectorตอนนี้NB ยังเป็นการSeqใช้งานเริ่มต้น
Connor Doyle

2

ฉันชอบรายการเสมอและฉันใช้ "พับ / ลด" ก่อน "เพื่อความเข้าใจ" อย่างไรก็ตาม "เพื่อความเข้าใจ" เป็นที่ต้องการหากจำเป็นต้องมี "การพับ" ที่ซ้อนกัน การเรียกซ้ำเป็นทางเลือกสุดท้ายหากฉันไม่สามารถทำงานให้สำเร็จโดยใช้ "พับ / ลด / สำหรับ"

ดังนั้นสำหรับตัวอย่างของคุณฉันจะทำ:

((0 to 3) :\ List[Int]())(_ :: _)

ก่อนที่ฉันจะทำ:

(for (x <- 0 to 3) yield x).toList

หมายเหตุ: ฉันใช้ "foldRight (: \)" แทน "foldLeft (/ :)" ที่นี่เนื่องจากลำดับของ "_" s สำหรับเวอร์ชันที่ไม่โยน StackOverflowException ให้ใช้ "foldLeft" แทน


18
ฉันไม่เห็นด้วยอย่างยิ่ง รูปแบบที่คุณต้องการจะดูเหมือนเสียงของเส้น
Matt R

14
ฉันจะ? ฉันเรียนรู้ Haskell ครั้งแรกในปี 2542 และขลุกอยู่ที่ Scala เป็นเวลาสองปี ฉันคิดว่าการพับเป็นสิ่งที่ดี แต่ถ้าใช้การพับในสถานการณ์ใด ๆ ก็ตามจำเป็นต้องเขียนสัญลักษณ์วรรคตอนที่เป็นความลับฉันจะพิจารณาแนวทางอื่น
Matt R

11
@Matt R: ฉันเห็นด้วย มีบางอย่างที่หักโหมเกินไปและนี่คือหนึ่งในนั้น
ryeguy

8
@WalterChang ฉันชอบรูปลักษณ์ของอีโมติคอนเหล่านั้น เดี๋ยวก่อนรหัสนั้นหรือ? : P
David J.

4
การเรียก((0 to 3) :\ List[Int]())(_ :: _)อิโมติโคดนั้นยุติธรรมหรือไม่?
David J.


2

หมายเหตุ: คำตอบนี้เขียนขึ้นสำหรับ Scala เวอร์ชันเก่า

คลาสคอลเลคชัน Scala กำลังจะได้รับการออกแบบใหม่ในรุ่น Scala 2.8 ดังนั้นโปรดเตรียมพร้อมที่จะเปลี่ยนวิธีสร้างรายการในเร็ว ๆ นี้

อะไรคือวิธีที่เข้ากันได้กับการสร้างรายการข้างหน้า? ฉันไม่รู้เลยเพราะฉันยังไม่ได้อ่านเอกสาร 2.8

เอกสาร PDF ที่อธิบายการเปลี่ยนแปลงที่เสนอของคลาสคอลเลกชัน


2
การเปลี่ยนแปลงส่วนใหญ่อยู่ในวิธีการนำสิ่งต่างๆไปใช้ภายในและในขั้นสูงเช่นการคาดการณ์ วิธีสร้างรายการจะไม่ได้รับผลกระทบ
Marcus Downing

โอเคนั่นคือสิ่งที่ควรรู้ นอกจากนี้คุณจะได้รับผลกระทบหากคุณใช้คลาสใด ๆ ในแพ็คเกจ collection.jcl
André Laszlo

1

ในฐานะผู้พัฒนา Scala ใหม่ฉันได้เขียนการทดสอบเล็ก ๆ เพื่อตรวจสอบเวลาในการสร้างรายการด้วยวิธีการที่แนะนำข้างต้น ดูเหมือนว่า (สำหรับ (p <- (0 ถึง x)) ให้ผล p) เป็นวิธีที่เร็วที่สุด

import java.util.Date
object Listbm {

  final val listSize = 1048576
  final val iterationCounts = 5
  def getCurrentTime: BigInt = (new Date) getTime

  def createList[T] ( f : Int => T )( size : Int ): T = f ( size )

  // returns function time execution
  def experiment[T] ( f : Int => T ) ( iterations: Int ) ( size :Int ) : Int  = {

    val start_time = getCurrentTime
    for ( p <- 0 to iterations )  createList ( f ) ( size )
    return (getCurrentTime - start_time) toInt

  }

  def printResult ( f:  => Int ) : Unit = println ( "execution time " + f  )

  def main( args : Array[String] ) {


    args(0) match {

      case "for" =>  printResult ( experiment ( x => (for ( p <- ( 0 to x ) ) yield p) toList  ) ( iterationCounts ) ( listSize ) )
      case "range"  =>  printResult ( experiment ( x => ( 0 to x ) toList ) ( iterationCounts ) ( listSize ) )
      case "::" => printResult ( experiment ( x => ((0 to x) :\ List[Int]())(_ :: _) ) ( iterationCounts ) ( listSize ) )
      case _ => println ( "please use: for, range or ::\n")
    }
  }
}

0

เป็นเพียงตัวอย่างที่ใช้ collection.breakOut

scala> val a : List[Int] = (for( x <- 1 to 10 ) yield x * 3)(collection.breakOut)
a: List[Int] = List(3, 6, 9, 12, 15, 18, 21, 24, 27, 30)

scala> val b : List[Int] = (1 to 10).map(_ * 3)(collection.breakOut)
b: List[Int] = List(3, 6, 9, 12, 15, 18, 21, 24, 27, 30)

0

ในการสร้างรายการสตริงให้ใช้สิ่งต่อไปนี้:

val l = List("is", "am", "are", "if")

1
เมื่อตอบคำถามที่เก่ากว่านี้ (10 ปี) และด้วยคำตอบที่มีอยู่มากมาย (9) การอธิบายว่าทำไมคำตอบของคุณจึงแตกต่างจากคำตอบอื่น ๆ ทั้งหมด ดูเหมือนว่าคุณไม่เข้าใจคำถาม
jwvh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.