Cara yang disukai untuk membuat daftar Scala


117

Ada beberapa cara untuk membuat daftar yang tidak dapat diubah di Scala (lihat kode contoh yang dibuat-buat di bawah). Anda dapat menggunakan ListBuffer yang dapat berubah, membuat vardaftar dan memodifikasinya, menggunakan metode rekursif ekor , dan mungkin metode lain yang tidak saya ketahui.

Secara naluriah, saya menggunakan ListBuffer, tetapi saya tidak punya alasan bagus untuk melakukannya. Adakah metode yang disukai atau idiomatik untuk membuat daftar, atau adakah situasi yang paling baik untuk satu metode di atas yang lain?

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

Jawaban:


108

ListBufferadalah daftar yang bisa berubah yang memiliki penambahan waktu konstan, dan konversi waktu konstan ke a List.

List tidak dapat diubah dan memiliki awal waktu konstan dan penambahan waktu linier.

Bagaimana Anda menyusun daftar Anda bergantung pada algoritme yang akan Anda gunakan untuk daftar tersebut dan urutan Anda mendapatkan elemen untuk membuatnya.

Misalnya, jika Anda mendapatkan elemen dalam urutan yang berlawanan dengan kapan elemen tersebut akan digunakan, Anda dapat menggunakan a Listdan melakukan prepend. Apakah Anda akan melakukannya dengan fungsi rekursif ekor foldLeft, atau sesuatu yang lain tidak terlalu relevan.

Jika Anda mendapatkan elemen dalam urutan yang sama saat Anda menggunakannya, a ListBufferkemungkinan besar adalah pilihan yang lebih disukai, jika performa sangat penting.

Tetapi, jika Anda tidak berada di jalur kritis dan masukan cukup rendah, Anda selalu dapat reversemembuat daftar nanti, atau hanya foldRight, atau reversemasukan, yang merupakan waktu linier.

Apa yang TIDAK Anda lakukan adalah menggunakan Listdan menambahkannya. Ini akan memberi Anda kinerja yang jauh lebih buruk daripada hanya prapending dan membalikkan di bagian akhir.


What you DON'T do is use a List and append to itApakah itu karena daftar baru dibuat? Padahal, menggunakan operasi awal tidak akan membuat daftar baru?
Kevin Meredith

2
@KevinKevin Tambahkan O (n), awalan adalah O (1).
Daniel C. Sobral

@pgoggijr Itu tidak benar. Pertama, tidak ada "perubahan" di mana pun, karena tidak dapat diubah. Traversal diperlukan karena semua elemen harus disalin, sehingga salinan elemen terakhir dapat dibuat dengan menunjuk ke elemen baru, bukan Nil. Kedua, tidak ada salinan apa pun pada prepend: sebuah elemen dibuat menunjuk ke daftar yang ada, dan hanya itu.
Daniel C. Sobral


22

Uhmm .. ini tampak terlalu rumit bagiku. Bolehkah saya melamar

def listTestD = (0 to 3).toList

atau

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

Terima kasih atas jawabannya, tetapi pertanyaannya adalah apa yang Anda lakukan dalam kasus yang tidak sepele. Saya memberi komentar di kode yang menjelaskan bahwa mereka semua setara dengan 0 hingga 3 toList.
agilefall

Ups, maaf kalau begitu! Terus terang, saya tidak pernah menggunakan ListBuffer.
Alexander Azarov

5

Anda ingin fokus pada kekekalan di Scala secara umum dengan menghilangkan vars. Keterbacaan masih penting bagi sesama pria jadi:

Mencoba:

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)

Anda bahkan mungkin tidak perlu mengonversi ke daftar dalam banyak kasus :)

Urutan yang diindeks akan memiliki semua yang Anda butuhkan:

Artinya, Anda sekarang dapat mengerjakan IndexedSeq itu:

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

NB Vectorsekarang juga merupakan Seqimplementasi default .
Connor Doyle

2

Saya selalu lebih suka Daftar dan saya menggunakan "lipat / kurangi" sebelum "untuk pemahaman". Namun, "untuk pemahaman" lebih disukai jika "lipatan" bertingkat diperlukan. Rekursi adalah pilihan terakhir jika saya tidak dapat menyelesaikan tugas menggunakan "lipat / kurangi / untuk".

jadi untuk contoh Anda, saya akan melakukan:

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

sebelum saya melakukannya:

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

Catatan: Saya menggunakan "foldRight (: \)" daripada "foldLeft (/ :)" di sini karena urutan "_". Untuk versi yang tidak menampilkan StackOverflowException, gunakan "foldLeft" sebagai gantinya.


18
Saya sangat tidak setuju; bentuk pilihan Anda terlihat seperti derau garis.
Matt R

14
Akankah saya? Saya pertama kali belajar Haskell pada tahun 1999, dan telah mencoba-coba Scala selama beberapa tahun. Saya pikir lipatan itu bagus, tetapi jika menerapkan lipatan dalam situasi tertentu membutuhkan penulisan string simbol tanda baca yang samar, saya akan mempertimbangkan pendekatan yang berbeda.
Matt R

11
@Matt R: Saya setuju. Ada yang namanya berlebihan, dan ini salah satunya.
ryeguy

8
@WalterChang Saya suka tampilan semua emotikon itu. Tunggu sebentar, apakah itu kode? : P
David J.

4
Apakah adil untuk memanggil ((0 to 3) :\ List[Int]())(_ :: _)emoticode?
David J.

2

Menggunakan List.tabulate, seperti ini,

List.tabulate(3)( x => 2*x )
res: List(0, 2, 4)

List.tabulate(3)( _ => Math.random )
res: List(0.935455779102479, 0.6004888906328091, 0.3425278797788426)

List.tabulate(3)( _ => (Math.random*10).toInt )
res: List(8, 0, 7)

2

Catatan: Jawaban ini ditulis untuk Scala versi lama.

Kelas koleksi Scala akan didesain ulang mulai dari Scala 2.8, jadi bersiaplah untuk segera mengubah cara Anda membuat daftar.

Apa cara kompatibel ke depan untuk membuat List? Saya tidak tahu karena saya belum membaca 2.8 dokumen.

Dokumen PDF yang menjelaskan perubahan yang diusulkan dari kelas koleksi


2
Sebagian besar perubahan terjadi pada cara penerapannya secara internal, dan dalam hal-hal lanjutan seperti proyeksi. Cara Anda membuat daftar tidak terpengaruh.
Marcus Downing

Oke, senang mengetahuinya. Anda juga akan terpengaruh jika Anda menggunakan kelas apa pun dalam paket collection.jcl.
André Laszlo

1

Sebagai pengembang skala baru, saya menulis tes kecil untuk memeriksa waktu pembuatan daftar dengan metode yang disarankan di atas. Sepertinya (untuk (p <- (0 ke x)) menghasilkan p) untukMencantumkan pendekatan tercepat.

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

hanya contoh yang menggunakan 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

Untuk membuat daftar string, gunakan yang berikut ini:

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

1
Saat menjawab pertanyaan yang sudah berumur 10 tahun ini, dan dengan begitu banyak jawaban yang ada (9), adalah praktik yang baik untuk menjelaskan mengapa jawaban Anda berbeda dari yang lain. Sepertinya Anda tidak memahami pertanyaan tersebut.
jwvh
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.