web-dev-qa-db-fra.com

Scala concaténation de liste, ::: vs ++

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?

337

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.

301
Daniel C. Sobral

::: 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.

82
paradigmatic

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 Lists. 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
78
ZhekaKozlov

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.

20
Mikaël Mayer