signo - ¿Cuál es la diferencia entre `::` y `+:` para anteponer a una lista)?
super hiper mega ultra recontra (2)
List
tiene 2 métodos que se especifican para anteponer un elemento a una lista (inmutable):
-
+:
(implementandoSeq.+:
, y -
::
(definido solo en laList
)
+:
técnicamente tiene una firma de tipo más general-
def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That
def ::[B >: A](x: B): List[B]
- pero ignorando lo implícito, que de acuerdo con el mensaje de doc simplemente requiere That
sea List[B]
, las firmas son equivalentes.
¿Cuál es la diferencia entre List.+:
Y List.::
? Si son de hecho idénticos, supongo que +:
sería preferible evitarlos según la List
implementación concreta. Pero ¿por qué se definió otro método público y cuándo lo llamaría el código del cliente?
Editar
También hay un extractor para ::
coincidencia de patrones, pero me pregunto acerca de estos métodos particulares.
Ver también: concatenación de lista Scala, ::: vs ++
La mejor forma de determinar la diferencia entre ambos métodos es buscarlo en el código fuente.
La source de ::
def ::[B >: A] (x: B): List[B] =
new scala.collection.immutable.::(x, this)
La source de +:
:
override def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That = bf match {
case _: List.GenericCanBuildFrom[_] => (elem :: this).asInstanceOf[That]
case _ => super.+:(elem)(bf)
}
Como puede ver, para List
, ambos métodos hacen uno y el mismo (el compilador elegirá List.canBuildFrom para el argumento CanBuildFrom
).
Entonces, ¿qué método usar? Normalmente uno elegiría la interfaz ( +:
que la implementación ( ::
pero como List
es una estructura de datos general en lenguajes funcionales, tiene sus propios métodos que son ampliamente utilizados. Muchos algoritmos se construyen de la misma forma que funciona List
. Por ejemplo, encontrará muchos métodos que anteponen elementos individuales a List
o llaman a los métodos de head
o tail
convenientes porque todas estas operaciones son O(1)
. Por lo tanto, si trabaja localmente con una List
(dentro de métodos o clases individuales), no hay problema para elegir los métodos específicos de la List
. Pero si desea comunicarse entre clases, es decir, si desea escribir algunas interfaces, debe elegir la interfaz Seq
más general.
+:
es más genérico, ya que permite que el tipo de resultado sea diferente del tipo de objeto al que se llama. Por ejemplo:
scala> Range(1,4).+:(0)
res7: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 2, 3)