2010-05-13 16 views
39

En Scala, PUEDO caseclass, case class Foo(x:Int), y luego ponerlo en una lista así:Operador '::' de Scala, ¿cómo funciona?

List(Foo(42)) 

Ahora, nada extraño aquí. Lo siguiente es extraño para mi El operador :: es una función en una lista, ¿verdad? Con cualquier función con un argumento en Scala, puedo llamarlo con notación infija. Un ejemplo es 1 + 2 es una función (+) en el objeto Int. La clase Foo que acabo de definir no tiene el operador ::, entonces, ¿cómo es posible?

Foo(40) :: List(Foo(2)) 

En Scala 2.8 RC1, me sale el siguiente salida desde el modo interactivo:

scala> case class Foo(x:Int) 
defined class Foo 

scala> Foo(40) :: List(Foo(2)) 
res2: List[Foo] = List(Foo(40), Foo(2)) 

puedo seguir y lo uso, pero ¿cuál es la explicación?

Respuesta

39

Desde el Spec:

6.12.3 InfixOperations un operador infijo puede ser una identificador arbitrario. Los operadores Infix tienen precedencia y asociatividad definidos de la siguiente manera.

...

La asociatividad de un operador es determinado por última carácter del operador. Los operadores que terminan en un punto ':' son de asociación correcta. Todos los demás operadores de son asociativos de izquierda.

Siempre se puede ver cómo se aplican estas reglas en Scala imprimiendo el programa después de que haya pasado por la fase de 'Typer' del compilador:

scala -Xprint:typer -e "1 :: Nil" 

val r: List[Int] = { 
    <synthetic> val x$1: Int = 1; 
    immutable.this.Nil.::[Int](x$1) 
}; 
+1

También se aplica para pasar parámetros de tipo para escribir constructores. Supongamos que tiene una clase de caso :: [H, T] (encabezado: H, cola: T); y clase SomeType [A]; entonces podrías hacer tanto SomeType [:: [String, Int]] ("a", 3) como SomeType [H :: T] ("a", 3) – lisak

19

Termina con un :. Y ese es el signo, que esta función se define en la clase de la derecha (en la clase List aquí).

Por lo tanto, es List(Foo(2)).::(Foo(40)), no Foo(40).::(List(Foo(2))) en su ejemplo.

+4

En otras palabras, 'un ::::: b' (sólo para estar tonto) es lo mismo que' b. :::: :(a) '. Además, los métodos cuyos nombres terminan en dos puntos y se usan en estilo infijo se asocian a la derecha, en lugar de a la izquierda, por lo que 'a :: b :: c' es lo mismo que' c.::(b.::(a))) ' –

12

La clase Foo que se acaba de definir no hace tienen el operador ::, así que ¿cómo es posible la siguiente:

Foo(40) :: List(Foo(2))

Si el nombre del método termina con dos puntos (:) el método se invoca en el operando derecho, que es el caso aquí. Si el nombre del método no termina con dos puntos, el método se invoca en el operando izquierdo. Por ejemplo, a + b, + se invoca en a.

Por lo tanto, en su ejemplo, :: es un método en su operando derecho, que es List.

15

Uno de los aspectos que faltan en las respuestas dadas es que para apoyar :: en las expresiones de coincidencia de patrones:

List(1,2) match { 
    case x :: xs => println(x + " " + xs) 
    case _ => println("") 
} 

A class :: is defined:

final case class ::[B](private var hd: B, private[scala] var tl: List[B]) 

por lo case ::(x,xs) produciría el mismo resultado. La expresión case x :: xs funciona porque el extractor predeterminado :: se define para la clase de caso y se puede usar infix.

Cuestiones relacionadas