Scala listesi birleştirme, ::: vs ++


362

Scala'da listeleri birleştirmek :::ve ++birleştirmek için bir fark var mı ?

scala> List(1,2,3) ++ List(4,5)
res0: List[Int] = List(1, 2, 3, 4, 5)

scala> List(1,2,3) ::: List(4,5)
res1: List[Int] = List(1, 2, 3, 4, 5)

scala> res0 == res1
res2: Boolean = true

Gönderen belgelerine gibi görünüyor ++oysa daha geneldir :::olduğunu List-özel. İkincisi diğer işlevsel dillerde kullanıldığı için sağlandı mı?


4
Ayrıca :::ile başlayan tüm yöntemler gibi bir önek operatörü:
Ben Jackson

3
Cevaplar Scala'daki listelerin ve operatör tekdüzeliğinin (ya da ikincisinin eksikliğinin) etrafında scala'nın evrimleşme şeklini hemen hemen anlatıyor. Bu kadar basit bir şeyin, herhangi bir Scala öğrencisinin zamanını karıştırmak ve boşa harcamak için böylesine uzun bir minutiae kuyruğuna sahip olması biraz talihsiz bir durumdur. Keşke 2.12'de düzeltilseydi.
matanster

Yanıtlar:


321

Legacy. Liste başlangıçta işlevsel dil görünümlü olarak tanımlanmıştı:

1 :: 2 :: Nil // a list
list1 ::: list2  // concatenation of two lists

list match {
  case head :: tail => "non-empty"
  case Nil          => "empty"
}

Elbette, Scala diğer koleksiyonları geçici olarak geliştirdi. 2.8 çıktığında, koleksiyonlar maksimum kod yeniden kullanımı ve tutarlı API için yeniden tasarlandı, böylece herhangi bir iki koleksiyonu ve hatta yineleyicileri ++birleştirmek için kullanabilirsiniz . Ancak List, kullanımdan kaldırılan bir ya da ikisinin dışında orijinal operatörlerini saklamalıdır.


19
Şimdi :::lehine kaçınmak en iyi yöntem ++midir? Ayrıca kullanmak +:yerine ::?
Luigi Plinge

37
::desen eşleşmesi nedeniyle yararlıdır (Daniel ikinci örneğine bakınız). Bunu ile yapamazsın+:
paradigmatik

1
@Luigi ListBunun yerine kullanıyorsanız Seq, deyimsel Listyöntemleri de kullanabilirsiniz . Öte yandan, bu zor hale getirecektir değiştirmek Hiç bunu arzu ederseniz, başka bir türe.
Daniel C. Sobral

2
Bir liste hem deyimsel işlemleri ( ::ve gibi :::) hem de diğer koleksiyonlar için ortak olan daha genel bir işlem olması iyi buluyorum . Her iki işlemi de dilden düşürmezdim.
Giorgio

21
@paradigmatic Scala 2.10 :+ve +:nesne çıkarıcılara sahiptir.
0__

97

Her zaman kullanın :::. Bunun iki nedeni vardır: verimlilik ve tip güvenliği.

verim

x ::: y ::: zdaha hızlıdır x ++ y ++ z, çünkü :::doğru çağrışımdır. x ::: y ::: z, x ::: (y ::: z)algoritmik olarak daha hızlı olan (x ::: y) ::: z(ikincisi O (| x |) daha fazla adım gerektirir) olarak ayrıştırılır .

Tip güvenliği

İle :::sadece iki Lists birleştirebilirsiniz . İle ++herhangi bir koleksiyon ekleyebilirsiniz List, ki bu korkunç:

scala> List(1, 2, 3) ++ "ab"
res0: List[AnyVal] = List(1, 2, 3, a, b)

++ile karıştırmak da kolaydır +:

scala> List(1, 2, 3) + "ab"
res1: String = List(1, 2, 3)ab

9
Sadece 2 listeyi birleştirirken, hiçbir fark yoktur, ancak 3 veya daha fazla durumda, iyi bir noktaya sahipsiniz ve hızlı bir kıyaslama ile onayladım. Bununla birlikte, verimlilik konusunda endişeleriniz varsa, x ::: y ::: zbununla değiştirilmelidir List(x, y, z).flatten. pastebin.com/gkx7Hpad
Luigi Plinge

3
Lütfen sol ilişkisel birleşmenin neden daha fazla O (x) adımı gerektirdiğini açıklayın. İkisinin de O (1) için çalıştığını düşündüm.
Pacman

6
@pacman Listeler birbiriyle bağlantılıdır, bir listeyi diğerine eklemek için, sonunda ikinci listenin eklendiği ilk listenin bir kopyasını oluşturmanız gerekir. Bu nedenle, birleşme, ilk listedeki elemanların sayısı bakımından O (n) 'dir. İkinci listenin uzunluğu çalışma zamanını etkilemez, bu nedenle uzun bir listeye kısa bir liste eklemek yerine kısa bir listeye uzun bir liste eklemek daha iyidir.
puhlen

1
@pacman Scala'nın listeleri değişmez . Bu yüzden birleştirme yaparken sadece son bağlantıyı değiştiremeyiz. Sıfırdan yeni bir liste oluşturmalıyız.
ZhekaKozlov

4
@pacman Karmaşıklık her zaman uzunluğu boyunca doğrusaldır xve y( zhiçbir durumda yinelenmez, bu nedenle çalışma süresi üzerinde hiçbir etkisi yoktur, bu nedenle kısa bir listeye uzun bir liste eklemek için daha iyidir) ancak asimptotik karmaşıklık tüm hikayeyi anlatmaz. x ::: (y ::: z)tekrarlar yve ekler z, sonra tekrarlar xve sonucunu ekler y ::: z. xve yikisi de bir kez yinelenir. (x ::: y) ::: zyineler xve ekler y, sonra sonucunu x ::: yve ekler z. yhala bir kez yinelenir x, ancak bu durumda iki kez yinelenir.
puhlen

84

:::yalnızca listelerle çalışır, ancak ++herhangi bir gezilebilir ile kullanılabilir. Mevcut uygulamada (2.9.0) olarak, ++geri düşer :::argüman da ise List.


4
Bu yüzden hem ::: hem de ++ listeyle birlikte çalışmak çok kolaydır. Bu potansiyel olarak kod / stile karışıklık getirebilir.
ses

24

Farklı bir nokta, ilk cümlenin şu şekilde ayrıştırılmasıdır:

scala> List(1,2,3).++(List(4,5))
res0: List[Int] = List(1, 2, 3, 4, 5)

İkinci örnek şu şekilde ayrıştırılır:

scala> List(4,5).:::(List(1,2,3))
res1: List[Int] = List(1, 2, 3, 4, 5)

Eğer makro kullanıyorsanız dikkatli olmalısınız.

Ayrıca, ++iki liste için çağırıyor :::ama daha fazla yük ile çünkü listeden Listeye bir yapıcı olması için örtük bir değer istiyor. Ancak mikrobenchmarks bu anlamda yararlı bir şey kanıtlamadı, sanırım derleyici bu aramaları optimize ediyor.

Isındıktan sonra Mikro Deneyler.

scala>def time(a: => Unit): Long = { val t = System.currentTimeMillis; a; System.currentTimeMillis - t}
scala>def average(a: () => Long) = (for(i<-1 to 100) yield a()).sum/100

scala>average (() => time { (List[Int]() /: (1 to 1000)) { case (l, e) => l ++ List(e) } })
res1: Long = 46
scala>average (() => time { (List[Int]() /: (1 to 1000)) { case (l, e) => l ::: List(e ) } })
res2: Long = 46

Daniel C. Sobrai'nin dediği gibi, herhangi bir koleksiyonun içeriğini kullanarak bir listeye ekleyebilirsiniz ++, oysa :::sadece listeleri birleştirebilirsiniz.


20
Lütfen aşırı basit olmayan mikrobenkmarklarınızı gönderin ve onları oylayacağım.
Mikaël Mayer
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.