Existe-t-il une différence entre :::
et ++
pour concaténer des listes dans 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
D'après la documentation , il semble que ++
soit plus général alors que :::
est spécifique à List
. Ce dernier est-il fourni parce qu'il est utilisé dans d'autres langages fonctionnels?
Héritage. La liste a été définie à l'origine pour être à la recherche de langages fonctionnels:
1 :: 2 :: Nil // a list
list1 ::: list2 // concatenation of two lists
list match {
case head :: tail => "non-empty"
case Nil => "empty"
}
Bien sûr, Scala a développé d'autres collections de manière ad hoc. Lorsque la version 2.8 est sortie, les collections ont été repensées pour une réutilisation maximale du code et une API cohérente. Vous pouvez ainsi utiliser ++
pour concaténer toute deux collections - et même des itérateurs. List devait cependant conserver ses opérateurs d'origine, à l'exception d'un ou deux qui étaient devenus obsolètes.
:::
ne fonctionne qu'avec des listes, alors que ++
peut être utilisé avec n'importe quel objet parcourable. Dans l'implémentation actuelle (2.9.0), ++
revient sur :::
si l'argument est également un List
.
Toujours utiliser :::
. Il y a deux raisons: l'efficacité et la sécurité du type.
efficacité
x ::: y ::: z
est plus rapide que x ++ y ++ z
, car :::
est associatif à droite. x ::: y ::: z
est analysé comme suit: x ::: (y ::: z)
, qui est algorithmiquement plus rapide que (x ::: y) ::: z
(ce dernier requiert plus de O (| x |) étapes).
Type sécurité
Avec :::
, vous ne pouvez concaténer que deux List
s. Avec ++
vous pouvez ajouter n’importe quelle collection à List
, ce qui est terrible:
scala> List(1, 2, 3) ++ "ab"
res0: List[AnyVal] = List(1, 2, 3, a, b)
++
est également facile à confondre avec +
:
scala> List(1, 2, 3) + "ab"
res1: String = List(1, 2, 3)ab
Un point différent est que la première phrase est analysée comme suit:
scala> List(1,2,3).++(List(4,5))
res0: List[Int] = List(1, 2, 3, 4, 5)
Considérant que le deuxième exemple est analysé comme suit:
scala> List(4,5).:::(List(1,2,3))
res1: List[Int] = List(1, 2, 3, 4, 5)
Donc, si vous utilisez des macros, vous devez en prendre soin.
En outre, ++
pour deux listes appelle :::
mais avec plus de temps système car il demande une valeur implicite pour qu'un générateur de liste à liste soit créé. Mais les microbenchmarks ne prouvent rien d’utile dans ce sens, je suppose que le compilateur optimise de tels appels.
Micro-repères après l'échauffement.
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
Comme l'a dit Daniel C. Sobrai, vous pouvez ajouter le contenu de toute collection à une liste à l'aide de ++
, tandis qu'avec :::
vous ne pouvez concaténer que des listes.