La respuesta corta es que la asociatividad correcta puede mejorar la legibilidad al hacer que el programador tipo sea consistente con lo que realmente hace el programa.
Por lo tanto, si escribe '1 :: 2 :: 3
', obtiene una Lista (1, 2, 3), en lugar de volver a obtener una Lista en un orden completamente diferente.
Eso sería porque '1 :: 2 :: 3 :: Nil
' es en realidad
List[Int].3.prepend(2).prepend(1)
scala> 1 :: 2 :: 3:: Nil
res0: List[Int] = List(1, 2, 3)
que es tanto:
- más legible
- más eficiente (O (1) para
prepend
, frente a O (n) para un hipotético append
método)
(Recordatorio, extracto del libro Programming in Scala)
Si se utiliza un método en notación de operador, como a * b
, el método se invoca en el operando izquierdo, como en a.*(b)
, a menos que el nombre del método termine en dos puntos.
Si el nombre del método termina en dos puntos, el método se invoca en el operando derecho.
Por lo tanto, en 1 :: twoThree
, se invoca el método ::
en twoThree
, pasando en 1, así: twoThree.::(1)
.
Para Lista, que desempeña el papel de una operación de anexión (la lista parece ser añadido después de que el '1' para formar '1 2 3
', cuando en realidad es 1, que es antepuesto a la lista).
Lista de clase no ofrece una verdadera operación de anexión, ya que el tiempo que se necesita para añadir a una lista crece linealmente con el tamaño de la lista, mientras que anteponiendo con :: toma tiempo constante.
myList :: 1
trataría de anteponer el contenido completo de miLista a '1', lo que sería más largo que anteponiendo 1 a Mi lista (como en '1 :: myList
')
Nota: No importa lo que la asociatividad un operador tiene, sin embargo, su los operandos son siempre evaluados de izquierda a derecha.
Así que si B es una expresión que no es sólo una simple referencia a un valor inmutable, a continuación, una ::: B se trata con mayor precisión como el bloque siguiente:
{ val x = a; b.:::(x) }
En este bloque de una imagen fija se evalúa antes b, y luego el resultado de esta evaluación se pasa como un operando al método b :::.
¿por qué hacer la distinción entre los métodos de izquierda y derecha asociativos-asociativos en absoluto?
Que permite mantener la apariencia de una operación asociativa izquierda usual ('1 :: myList
') mientras se aplica la operación en la expresión correcta porque;
- es más eficiente.
- pero es más fácil de leer con una orden asociativo inversa ('
1 :: myList
'vs' myList.prepend(1)
')
Así como usted dice, 'azúcar sintáctico', por lo que yo sé.
Tenga en cuenta, en el caso de foldLeft
, por ejemplo, que podría tener gone a little to far (con la '/:
' equivalente operador asociativo por la derecha)
Para incluir algunos de sus comentarios, ligeramente reformulada:
si considera una función 'agregar', asociativa a la izquierda, entonces debería escribir 'oneTwo append 3 append 4 append 5
'.
Sin embargo, si se tratara de anexar 3, 4, y 5 a oneTwo (que asumiría por la forma en que está escrito), sería O (N).
mismo con '::' si era para "añadir". Pero no lo es. En realidad, es para "anteponer"
Eso significa 'a :: b :: Nil
' es para 'List[].b.prepend(a)
'
Si '::' fueron precedidos y sin embargo seguir siendo asociativos por la izquierda, a continuación, la lista resultante estaría en el orden equivocado .
Se esperaría que devuelva List (1, 2, 3, 4, 5), pero terminaría devolviendo List (5, 4, 3, 1, 2), lo que podría ser inesperado para el programador.
Eso se debe a que, lo que ha hecho habría sido, en el orden asociativo por la izquierda:
(1,2).prepend(3).prepend(4).prepend(5) : (5,4,3,1,2)
Así, la asociatividad por la derecha hace que la coincidencia de código con el orden real del valor de retorno.
Acaba de incluir sus comentarios en mi respuesta, con algunos ejemplos para ilustrarlos. – VonC
Por lo que puedo decir, es para hacer que los usuarios de Lisp se sientan como en casa, simplemente para permitir la :: lista anexar la operación. – skaffman
@skaffman no, no lo es. lee la respuesta. –