Cách ưa thích để tạo danh sách Scala


117

Có một số cách để tạo danh sách bất biến trong Scala (xem mã ví dụ có sẵn bên dưới). Bạn có thể sử dụng ListBuffer có thể thay đổi, tạo một vardanh sách và sửa đổi nó, sử dụng phương thức đệ quy đuôi và có thể là những phương thức khác mà tôi không biết.

Theo bản năng, tôi sử dụng ListBuffer, nhưng tôi không có lý do chính đáng để làm như vậy. Có phương pháp ưa thích hoặc phương pháp thành ngữ nào để tạo danh sách hay có những tình huống tốt nhất cho phương pháp này hơn phương pháp khác không?

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

Câu trả lời:


108

ListBuffer là một danh sách có thể thay đổi có phần nối thêm thời gian không đổi và chuyển đổi thời gian không đổi thành List .

List là không thay đổi và có phần phụ trước thời gian không đổi và phần phụ thời gian tuyến tính.

Cách bạn xây dựng danh sách của mình phụ thuộc vào thuật toán bạn sẽ sử dụng danh sách và thứ tự mà bạn lấy các phần tử để tạo danh sách.

Ví dụ: nếu bạn nhận được các phần tử theo thứ tự ngược lại với thời điểm chúng sẽ được sử dụng, thì bạn chỉ có thể sử dụng phần trước a Listvà do. Cho dù bạn sẽ làm như vậy với một hàm đệ quy đuôi,foldLeft hay một cái gì đó khác không thực sự phù hợp.

Nếu bạn nhận được các phần tử theo cùng thứ tự mà bạn sử dụng chúng, thì a ListBufferrất có thể là lựa chọn thích hợp hơn, nếu hiệu suất là rất quan trọng.

Tuy nhiên, nếu bạn không ở trên con đường quan trọng và đầu vào đủ thấp, bạn luôn có thể reverseliệt kê sau, hoặc chỉ foldRight, hoặc reverseđầu vào, là thời gian tuyến tính.

Điều bạn KHÔNG NÊN làm là sử dụng một Listvà thêm vào nó. Điều này sẽ mang lại cho bạn hiệu suất kém hơn nhiều so với chỉ chi tiêu trước và đảo ngược vào cuối.


What you DON'T do is use a List and append to itĐó có phải là do một danh sách mới được tạo? Trong khi, sử dụng thao tác thêm trước sẽ không tạo danh sách mới?
Kevin Meredith

2
@KevinMeredith Có. Nối thêm là O (n), thêm trước là O (1).
Daniel C. Sobral,

@pgoggijr Điều đó không đúng. Đầu tiên, không có "thay đổi" ở bất cứ đâu, bởi vì nó là bất biến. Yêu cầu truyền tải vì tất cả các phần tử phải được sao chép, chỉ vì vậy một bản sao của phần tử cuối cùng có thể được thực hiện trỏ đến một phần tử mới thay vì Nil. Thứ hai, không có bản sao của bất kỳ loại nào khi thêm trước: một phần tử được tạo trỏ đến danh sách hiện có, và thế là xong.
Daniel C. Sobral


22

Uhmm .. những thứ này có vẻ quá phức tạp với tôi. Tôi có thể cầu hôn

def listTestD = (0 to 3).toList

hoặc là

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

Cảm ơn vì câu trả lời, nhưng câu hỏi là bạn sẽ làm gì trong trường hợp không tầm thường. Tôi đặt một bình luận trong mã giải thích rằng tất cả chúng đều tương đương với 0 đến 3 toList.
nhanh nhẹn vào

Rất tiếc, xin lỗi! Thành thật mà nói, tôi không bao giờ sử dụng ListBuffer.
Alexander Azarov

5

Bạn muốn tập trung vào tính bất biến trong Scala nói chung bằng cách loại bỏ bất kỳ vars nào. Khả năng đọc vẫn quan trọng đối với đồng nghiệp của bạn vì vậy:

Thử:

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)

Bạn thậm chí có thể không cần phải chuyển đổi thành một danh sách trong hầu hết các trường hợp :)

Seq được lập chỉ mục sẽ có mọi thứ bạn cần:

Tức là bây giờ bạn có thể làm việc trên IndexedSeq đó:

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

NB Vectorbây giờ cũng là cài đặt mặc định Seq.
Connor Doyle

2

Tôi luôn thích Danh sách hơn và tôi sử dụng "gấp / giảm" trước "để hiểu". Tuy nhiên, "để hiểu" được ưu tiên hơn nếu yêu cầu các "nếp gấp" lồng nhau. Đệ quy là phương sách cuối cùng nếu tôi không thể hoàn thành tác vụ bằng cách sử dụng "gấp / giảm / cho".

vì vậy đối với ví dụ của bạn, tôi sẽ làm:

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

trước khi tôi làm:

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

Lưu ý: Tôi sử dụng "foldRight (: \)" thay vì "foldLeft (/ :)" ở đây vì thứ tự của "_" s. Đối với phiên bản không ném StackOverflowException, hãy sử dụng "foldLeft".


18
Tôi rất không đồng ý; hình thức ưa thích của bạn trông giống như nhiễu dòng.
Matt R

14
Tôi sẽ? Tôi học Haskell lần đầu tiên vào năm 1999, và đã học ở Scala được vài năm. Tôi nghĩ rằng các nếp gấp là tuyệt vời, nhưng nếu việc áp dụng một nếp gấp trong bất kỳ tình huống nhất định nào đòi hỏi phải viết một chuỗi ký hiệu dấu câu khó hiểu, tôi sẽ xem xét một cách tiếp cận khác.
Matt R

11
@Matt R: Tôi đồng ý. Có một thứ như là làm quá mức, và đây là một trong số chúng.
ryeguy 17/09/09

8
@WalterChang Tôi thích giao diện của tất cả các biểu tượng cảm xúc đó. Chờ một chút, đó có phải là mã không? : P
David J.

4
Có công bằng khi gọi là ((0 to 3) :\ List[Int]())(_ :: _)mã biểu tượng cảm xúc?
David J.

2

Sử dụng List.tabulate, như thế này,

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

Lưu ý: Câu trả lời này được viết cho phiên bản cũ của Scala.

Các lớp tập hợp Scala sẽ được thiết kế lại kể từ Scala 2.8, vì vậy hãy chuẩn bị để thay đổi cách bạn tạo danh sách sớm.

Cách tạo Danh sách tương thích về phía trước là gì? Tôi không biết vì tôi chưa đọc 2,8 tài liệu.

Tài liệu PDF mô tả các thay đổi được đề xuất của các lớp bộ sưu tập


2
Hầu hết các thay đổi nằm trong cách mọi thứ được thực hiện trong nội bộ và những thứ nâng cao như dự báo. Cách bạn tạo danh sách không bị ảnh hưởng.
Marcus Downing

Ok, thật tốt khi biết. Bạn cũng sẽ bị ảnh hưởng nếu sử dụng bất kỳ lớp nào trong gói collection.jcl.
André Laszlo

1

Là một nhà phát triển scala mới, tôi đã viết thử nghiệm nhỏ để kiểm tra thời gian tạo danh sách bằng các phương pháp được đề xuất ở trên. Có vẻ như (cho (p <- (0 đến x)) nhường p) Liệt kê cách tiếp cận nhanh nhất.

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

chỉ là một ví dụ sử dụng 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

Để tạo danh sách chuỗi, hãy sử dụng như sau:

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

1
Khi trả lời một câu hỏi đã cũ (10 năm) và với rất nhiều câu trả lời hiện có (9), bạn nên giải thích tại sao câu trả lời của bạn khác với tất cả những câu khác. Như hiện tại, có vẻ như bạn không hiểu câu hỏi.
jwvh
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.