Scala listesi oluşturmanın tercih edilen yolu


117

Scala'da değişmez bir liste oluşturmanın birkaç yolu vardır (aşağıdaki hazırlanmış örnek koda bakın). Değişken bir ListBuffer kullanabilir, bir varliste oluşturabilir ve onu değiştirebilir, bir tail recursive metodu ve muhtemelen bilmediğim diğerlerini kullanabilirsiniz.

İçgüdüsel olarak ListBuffer'ı kullanıyorum, ancak bunu yapmak için iyi bir nedenim yok. Liste oluşturmak için tercih edilen veya deyimsel bir yöntem var mı, yoksa bir yöntem için diğerine göre en iyi olan durumlar var mı?

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

Yanıtlar:


108

ListBuffersabit zamanlı ek ve sabit zamanlı bir List.

List değişmezdir ve sabit zamanlı başa ve doğrusal zaman ekine sahiptir.

Listenizi nasıl oluşturacağınız, listeyi kullanacağınız algoritmaya ve onu oluşturmak için öğeleri elde ettiğiniz sıraya bağlıdır.

Örneğin, öğeleri ne zaman kullanılacaklarının tersi sırayla alırsanız, o zaman sadece a kullanabilir Listve başa ekler yapabilirsiniz. Bunu bir kuyruk özyinelemeli işlevle mi foldLeftyoksa başka bir şeyle mi yapacağınız gerçekten alakalı değildir.

Öğeleri kullandığınız sırayla alırsanız ListBuffer, performans kritikse, a büyük olasılıkla tercih edilen bir seçimdir.

Ancak, kritik bir yolda değilseniz ve girdi yeterince düşükse reverse, listeyi her zaman daha sonra veya sadece foldRightveya reversedoğrusal zaman olan girdiyi yapabilirsiniz.

Ne YAPMAYIN yapmak is a kullanmak Listkendisine ve append. Bu size, sonunda sadece önceden harcamaktan ve geri almaktan çok daha kötü bir performans verecektir.


What you DON'T do is use a List and append to itBunun nedeni yeni bir listenin oluşturulması mı? Oysa başa ekleme işlemi kullanmak yeni bir liste oluşturmaz?
Kevin Meredith

2
@KevinMeredith Evet. Ek O (n), başa eklenen O (1).
Daniel C. Sobral

@pgoggijr Bu doğru değil. Birincisi, hiçbir yerde "değişim" yoktur, çünkü değişmezdir. Bir geçiş gereklidir çünkü tüm elemanların kopyalanması gerekir, sadece son elemanın bir kopyası yerine yeni bir elemana işaret edilerek yapılabilir Nil. İkincisi, başa eklenen herhangi bir türden kopya yoktur: mevcut listeyi gösteren bir öğe oluşturulur ve işte bu kadar.
Daniel C. Sobral


22

Uhmm .. bunlar bana çok karmaşık görünüyor. Teklif edebilir miyim

def listTestD = (0 to 3).toList

veya

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

Cevabınız için teşekkürler, ancak soru önemsiz olmayan durumda ne yapacağınızdır. Koda, hepsinin 0 ila 3 toList'e eşdeğer olduğunu açıklayan bir yorum koydum.
agilefall

Oops, pardon! Açıkçası asla ListBuffer kullanmam.
Alexander Azarov

5

Scala'da genel olarak değişkenleri ortadan kaldırarak değişmezliğe odaklanmak istiyorsunuz. Okunabilirlik dostunuz için hala önemlidir, bu nedenle:

Deneyin:

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)

Muhtemelen çoğu durumda bir listeye dönüştürmenize bile gerek yoktur :)

İndekslenen sıra ihtiyacınız olan her şeye sahip olacaktır:

Yani, artık IndexedSeq üzerinde çalışabilirsiniz:

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

NB Vectorartık aynı zamanda varsayılan Sequygulamadır.
Connor Doyle

2

Her zaman List'i tercih ederim ve "anlamak için" önce "katla / küçült" seçeneğini kullanırım. Bununla birlikte, iç içe geçmiş "kıvrımlar" gerekliyse "anlama için" tercih edilir. Görevi "katla / azalt / için" kullanarak başaramazsam, yineleme son çare.

yani sizin örneğiniz için yapacağım:

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

benden önce:

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

Not: Burada "_" s sırasına göre "foldLeft (/ :)" yerine "foldRight (: \)" kullanıyorum. StackOverflowException oluşturmayan bir sürüm için bunun yerine "foldLeft" kullanın.


18
Kesinlikle katılmıyorum; tercih ettiğiniz form sadece satır gürültüsü gibi görünür.
Matt R

14
Yapacak mıyım? Haskell'i ilk olarak 1999'da öğrendim ve birkaç yıldır Scala ile uğraşıyorum. Kıvrımların harika olduğunu düşünüyorum, ancak herhangi bir durumda bir katlama uygulamak, şifreli bir noktalama işaretleri dizisi yazmayı gerektiriyorsa, farklı bir yaklaşım düşünürüm.
Matt R

11
@Matt R: Katılıyorum. Aşırıya kaçmak diye bir şey var ve bu onlardan biri.
ryeguy

8
@WalterChang Tüm bu ifadelerin görünümünü beğendim. Bekle bir dakika, bu kod mu? : P
David J.

4
((0 to 3) :\ List[Int]())(_ :: _)Emoticode olarak adlandırmak adil mi?
David J.

2

Bunun List.tabulategibi kullanarak

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

Not: Bu cevap Scala'nın eski bir sürümü için yazılmıştır.

Scala koleksiyonu sınıfları Scala 2.8'den itibaren yeniden tasarlanacak, bu nedenle listeleri oluşturma şeklinizi çok yakında değiştirmeye hazır olun.

Liste oluşturmanın ileriye dönük uyumlu yolu nedir? Henüz 2.8 belgeyi okumadığım için hiçbir fikrim yok.

Koleksiyon sınıflarının önerilen değişikliklerini açıklayan bir PDF belgesi


2
Değişikliklerin çoğu, işlerin dahili olarak uygulanma biçiminde ve tahminler gibi gelişmiş şeylerde. Bir listeyi nasıl oluşturduğunuz etkilenmez.
Marcus Downing

Tamam, bunu bilmek güzel. Collection.jcl paketinde herhangi bir sınıf kullanırsanız da etkileneceksiniz.
André Laszlo

1

Yeni bir ölçek geliştiricisi olarak, liste oluşturma zamanını yukarıda önerilen yöntemlerle kontrol etmek için küçük bir test yazdım. En hızlı yaklaşımı ((p <- (0 - x) için) vermek p) toList'e benziyor.

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

sadece collection.breakOut kullanan bir örnek

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

Bir dizi listesi oluşturmak için aşağıdakileri kullanın:

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

1
Bu kadar eski (10 yıllık) bir soruyu yanıtlarken ve çok sayıda mevcut cevapla (9), cevabınızın neden diğerlerinden farklı olduğunu açıklamak iyi bir uygulamadır. Olduğu gibi, soruyu anlamamışsın gibi görünüyor.
jwvh
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.