it-swarm.com.de

Scala Listenverkettung, ::: vs ++

Gibt es einen Unterschied zwischen ::: und ++ zum Verketten von Listen in Scala?

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

Von der Dokumentation sieht es aus wie ++ ist allgemeiner, während ::: ist List- spezifisch. Wird letzteres bereitgestellt, weil es in anderen funktionalen Sprachen verwendet wird?

Erbe. Die Liste wurde ursprünglich so definiert, dass sie nach funktionalen Sprachen aussieht:

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

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

Natürlich hat Scala andere Sammlungen ad-hoc weiterentwickelt. Als 2.8 herauskam, wurden die Sammlungen für maximale Wiederverwendung von Code und konsistente API neu gestaltet, sodass Sie ++ zu verketten any zwei Sammlungen - und sogar Iteratoren.List muss jedoch seine ursprünglichen Operatoren behalten, abgesehen von einem oder zwei, die veraltet sind.

301

::: funktioniert nur mit Listen, während ++ kann mit jedem überfahrbaren benutzt werden. In der aktuellen Implementierung (2.9.0) wurde ++ fällt zurück auf ::: wenn das Argument auch ein List ist.

82
paradigmatic

Verwenden Sie immer :::. Es gibt zwei Gründe: Effizienz und Typensicherheit.

Effizienz

x ::: y ::: z Ist schneller als x ++ y ++ z, Weil ::: Richtig assoziativ ist. x ::: y ::: z Wird als x ::: (y ::: z) analysiert, was algorithmisch schneller ist als (x ::: y) ::: z (Letzteres erfordert O (| x |) mehr Schritte).

Typ Sicherheit

Mit ::: Können Sie nur zwei Lists verketten. Mit ++ Können Sie jede Sammlung an List anhängen, was schrecklich ist:

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

++ Kann auch leicht mit + Verwechselt werden:

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

Ein anderer Punkt ist, dass der erste Satz analysiert wird als:

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

Während das zweite Beispiel analysiert wird als:

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

Wenn Sie also Makros verwenden, sollten Sie vorsichtig sein.

Außerdem ruft ++ Für zwei Listen ::: Auf, jedoch mit mehr Aufwand, da ein impliziter Wert erforderlich ist, um einen Builder von Liste zu Liste zu haben. Aber Mikrobenchmarks haben sich in diesem Sinne nicht als nützlich erwiesen, ich vermute, dass der Compiler solche Aufrufe optimiert.

Micro-Benchmarks nach dem Aufwärmen.

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

Wie Daniel C. Sobrai sagte, können Sie den Inhalt jeder Sammlung mit ++ An eine Liste anhängen, während Sie mit ::: Nur Listen verketten können.

20
Mikaël Mayer