2011-10-08 10 views
9

¿Por qué esto no está funcionando:subrayado en List.filter

List(true,false).filter(_).size 

El error dice:

<console>:8: error: missing parameter type for expanded function 
((x$1) => List(true, false).filter(x$1).size) 
    List(true,false).filter(_).size 
          ^

pero las siguientes obras (se parece a lo mismo por mí):

List(true,false).filter(a => a).size 

Estoy usando Scala 2.9.0.1.

+0

ver también: [ámbito no degenerado más ajustado] (http://stackoverflow.com/questions/5259006/underscore-in-named-arguments/5259946#5259946) –

Respuesta

23

Manipulación de _ es un poco complicado en Scala y como nota al margen Creo que el manejo de errores debe ser mejorado un poco. Volver al tema, eche un vistazo a este ejemplo:

def twice(i: Int) = i * 2 
def gt2(j: Int) = j > 2 

List(1,2,3) filter gt2 

Esto compila bien y funciona como se esperaba. Sin embargo, al tratar de componer funciones se obtienen resultados con un error críptico:

List(1,2,3) filter gt2(twice(_)) 

    error: missing parameter type for expanded function ((x$1) => twice(x$1)) 
      List(1,2,3) filter gt2(twice(_)) 
            ^

¿Qué sucedió? Básicamente cuando el compilador de Scala ve subrayarlo, lo vincula al en el contexto más inmediato, que es twice(_) en este caso. Esto significa que ahora estamos llamando al gt2() con una función twice como argumento. Lo que el compilador sabe es que esta función toma un argumento y devuelve el mismo tipo. Podría decirse que debería averiguar el tipo de este argumento y el tipo de devolución es Int basado en la firma twice(), sin embargo, utiliza el marcador de posición x$1 temporalmente hasta que se da cuenta de eso más tarde.

Desafortunadamente no puede hacer eso ya que gt2() espera un Int mientras proporcionamos una función (al menos eso es lo que piensa el compilador).

Entonces, ¿por qué:

List(1,2,3) filter {k => gt2(twice(k))} 

trabajo? El compilador no conoce el tipo de k de antemano. Sin embargo, sabe que gt2 devuelve Boolean y espera un Int. También sabe que twice() espera un Int y devuelve uno también. De esta manera infiere el tipo de k. Por otro lado, el compilador sabe desde el principio que filter espera Int => Boolean.


Dicho esto, vuelve a su caso. El guión bajo solo ((_)) no se considera un "contexto" por lo que el compilador busca otro contexto de inclusión más inmediato.El !_ se habría considerado una función en sí misma, así como _ == true. Pero no el guión bajo solo.

Entonces, ¿cuál es el contexto inmediato más cercano (estoy seguro de que hay un nombre científico para eso ...) en este caso? Pues bien, el conjunto expresión:

(x$1) => List(true, false).filter(x$1).size 

El compilador supone que usted está tratando de crear una función que toma algún parámetro de tipo desconocido y devuelve algo del tipo de una expresión: List(true, false).filter(x$1).size. Nuevamente podría decirse que debería poder darse cuenta de que filter toma Boolean => Boolean y devuelve Int (.size), pero aparentemente no es así.


Entonces, ¿qué puedes hacer? Debe indicar al compilador que el guión bajo debe interpretarse en un contexto más pequeño. Puede decir:

List(true,false) filter (_ == true) 
List(true,false) filter (i => i) 
List(true,false) filter identity 
+0

Gran explicación. Puede que tenga que crear algunas cuentas falsas para votarlo un par de veces. –

+0

Teniendo en cuenta las muchas preguntas sobre este comportamiento, creo que en realidad es mejor que el compilador no descubra que una función '(Boolean => Boolean) => Int' cabría pero que más bien no compila. – Debilski

3

El primer error se debe a que Scala no sabe qué hacer con _. Así que trate de esto ...

List(true,false).filter(_:Boolean).size 

Después de eso, se obtiene más información:

<console>:8: error: type mismatch; 
found : Boolean 
required: (Boolean) => Boolean 
List(true,false).filter(_:Boolean).size 

Es sólo la evaluación de la _ al igual que el valor y no como la función. Por la ScalaDoc

filter (pred: (A) ⇒ Boolean): GenTraversable[A] 
2

Veo que el mensaje de error ha mejorado mucho. Veamos lo que el mensaje de error ha dicho que escribió, y su versión de trabajo:

((x$1) => List(true, false).filter(x$1).size) 
      List(true,false).filter(a => a).size 

O, ajustando espacios, paréntesis y nombres de variables:

a => List(true, false).filter(a ).size 
    List(true, false).filter(a => a).size 

Cómo se ve ahora mismo?

En pocas palabras, cuando pasa guion bajo en lugar de un parámetro, está haciendo partial function application. Probablemente esté más familiarizado con los guiones bajos que se utilizan como marcadores de posición para parámetros en funciones anónimas, que es lo que sucede cuando aparece en una expresión, como _ + 1. Estos dos usos son diferentes, incluso si ambos dan como resultado una función anónima.