Scala list concatenation,: vs ++

Czy jest jakaś różnica między ::: i ++ dla łączenia list w Scali?

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

Z dokumentacja wygląda na to, że ++ jest bardziej ogólna, podczas gdy ::: jest List-specyficzna. Czy ten ostatni jest dostępny, ponieważ jest używany w innych językach funkcyjnych?

Author: Jacek Laskowski, 2011-07-02

4 answers

Dziedzictwo. Lista została pierwotnie zdefiniowana jako functional-languages-looking:

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

list match {
  case head :: tail => "non-empty"
  case Nil          => "empty"
}
Oczywiście Scala ewoluowała inne kolekcje, w sposób ad hoc. Po wydaniu 2.8 kolekcje zostały przeprojektowane dla maksymalnego ponownego użycia kodu i spójnego API, dzięki czemu można użyć {[1] } do połączenia dowolnych dwóch kolekcji -- a nawet iteratorów. Lista, jednak, musiał zachować swoje oryginalne operatorów, oprócz jednego lub dwóch, które zostały przestarzałe.
 331
Author: Daniel C. Sobral,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-25 21:22:54

Zawsze używaj :::. Istnieją dwa powody: wydajność i bezpieczeństwo typu.

Wydajność

x ::: y ::: z jest szybszy niż x ++ y ++ z, Ponieważ {[2] } jest prawidłowy. {[3] } jest przetwarzane jako x ::: (y ::: z), co jest algorytmicznie szybsze niż (x ::: y) ::: z (ta ostatnia wymaga O (|x|) więcej kroków).

Bezpieczeństwo typu

Z ::: możesz połączyć tylko dwa List s. z ++ możesz dołączyć dowolny zbiór do List, co jest straszne:

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

++ jest również łatwy do wymieszania z +:

scala> List(1, 2, 3) + "ab"
res1: String = List(1, 2, 3)ab
 106
Author: ZhekaKozlov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-21 08:13:57

::: działa tylko z listami, podczas gdy ++ może być używany z dowolnym przejazdem. W obecnej implementacji (2.9.0), ++ wraca do :::, jeśli argument jest również List.

 84
Author: paradigmatic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-02 23:18:22

Inną kwestią jest to, że pierwsze zdanie jest przetwarzane jako:

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

Natomiast drugi przykład jest przetwarzany jako:

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

Więc jeśli używasz makr, należy zachować ostrożność.

Poza tym, ++ dla dwóch list wywołuje :::, ale z większym obciążeniem, ponieważ prosi o domyślną wartość, aby mieć konstruktora z listy do listy. Ale microbenchmarks nie udowodnił niczego użytecznego w tym sensie, myślę, że kompilator optymalizuje takie wywołania.

Mikro-Benchmarki po rozgrzaniu.

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

Jak powiedział Daniel C. Sobrai, możesz dodać zawartość dowolnej kolekcji do listy za pomocą ++, podczas gdy za pomocą ::: możesz tylko łączyć listy.

 25
Author: Mikaël Mayer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-02 19:26:41