2010-03-01 17 views
9

¿Por qué aparece un error cuando trato de usar _ en lugar de usar un identificador con nombre?En Scala, ¿cuál es la diferencia entre usar `_` y usar un identificador con nombre?

scala> res0 
res25: List[Int] = List(1, 2, 3, 4, 5) 

scala> res0.map(_=>"item "+_.toString) 
<console>:6: error: missing parameter type for expanded function ((x$2) => "item 
".$plus(x$2.toString)) 
     res0.map(_=>"item "+_.toString) 
         ^

scala> res0.map(i=>"item "+i.toString) 
res29: List[java.lang.String] = List(item 1, item 2, item 3, item 4, item 5) 

Respuesta

18

subraya utilizada en lugar de nombres de variables como que son especiales; el guión bajo enésimo significa el enésimo argumento para una función anónima. Así que los siguientes son equivalentes:

List(1, 2, 3).map(x => x + 1) 

List(1, 2, 3).map(_ + 1) 

Pero, si usted hace esto:

List(1, 2, 3).map(_ => _ + 1) 

Luego está correlacionando la lista con una función que ignora su único argumento y devuelve la función definida por _ + 1. (Este ejemplo específico no se compilará porque el compilador no puede inferir qué tipo tiene el segundo guión bajo). Un ejemplo equivalente, con parámetros con nombre se vería así:

List(1, 2, 3).map(x => { y => y + 1 }) 

En resumen, el uso de guiones en la lista de argumentos de una función significa "estoy ignorando estos argumentos en el cuerpo de esta función". Usarlos en el cuerpo significa "Compilador, genere una lista de argumentos para mí". Los dos usos no se mezclan muy bien.

+1

@Scoobie Solo para reforzar esto, el guion bajo se usa para muchos _different_ propósitos en Scala. Como David explicó, cada uso en tu ejemplo tiene un significado diferente. También hay otros significados: el guión bajo, en Scala, es un buen ejemplo de problemas que surgen de la sobrecarga del operador. Si bien tuve problemas al principio, puedo decir honestamente que nunca he pensado en alguna forma de mejorarlo. –

3

Si no va a enlazar un identificador, simplemente deje esa parte.

res0.map("item "+_.toString) 
4

Para complementar las otras respuestas, aquí hay algunos ejemplos que muestran por qué se obtiene el "tipo de parámetro faltante" en algunos casos cuando se usa '_' como parámetro de marcador de posición.

La inferencia de tipo de Scala considera el tipo 'esperado' de una expresión en función de su contexto. Si no hay contexto, no puede inferir el tipo de los parámetros. Observe en el mensaje de error que la primera y la segunda instancia de _ se reemplazan con los identificadores generados por el compilador x$1 y x$2.

scala> _ + _ 
<console>:5: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2)) 
     _ + _ 
    ^
<console>:5: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2)) 
     _ + _ 
     ^

Adición de una adscripción tipo a toda la expresión proporciona el contexto suficiente para ayudar al inferencer:

scala> (_ + _) : ((Int, Int) => Int) 
res3: (Int, Int) => Int = <function2> 

Alternativamente, se puede añadir una indicación de tipo de cada parámetro marcador de posición:

scala> (_: Int) + (_: Int)   
res4: (Int, Int) => Int = <function2> 

En la llamada de función a continuación con los argumentos de tipo provistos, el contexto no es ambiguo y se deduce el tipo de función.

scala> def bar[A, R](a1: A, a2: A, f: (A, A) => R) = f(a1, a2) 
bar: [A,R](a1: A,a2: A,f: (A, A) => R)R 

scala> bar[Int, Int](1, 1, _ + _) 
res5: Int = 2 

Sin embargo, si nos preguntamos el compilador para inferir los parámetros de tipo, si falla:

scala> bar(1, 1, _ + _)   
<console>:7: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2)) 
     bar(1, 1, _ + _) 
       ^
<console>:7: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2)) 
     bar(1, 1, _ + _) 
        ^

Podemos evitarlo, sin embargo, por ganarse las listas de parámetros. Aquí, los argumentos a la primera lista de parámetros (1, 1), indican que el parámetro de tipo A debe ser Int. Entonces sabe que el tipo del argumento f debe ser (Int, Int) => ?), y el tipo de retorno R se infiere como Int, el resultado de la suma entera. Verá el mismo enfoque utilizado en Traversable.flatMap en la biblioteca estándar.

scala> def foo[A, R](a1: A, a2: A)(f: (A, A) => R) = f(a1, a2) 
foo: [A,R](a1: A,a2: A)(f: (A, A) => R)R 

scala> foo[Int, Int](1, 1) { _ + _ } 
res1: Int = 2 

scala> foo(1, 1) { _ + _ } 
res0: Int = 2 
Cuestiones relacionadas