2011-01-18 24 views
5

Soy nuevo en scala y trato de escribir una función literal que verifique si un entero determinado es impar o no. mi primer intento es:cómo simplificar la función literal de Scala de esta manera?

val isOdd = (x:Int) => (x & 1) == 1

funciona muy bien, y, puesto que el parámetro sólo aparece una vez x dentro de esta función literal, estoy tentado a usar el "_" notación para simplificar aún más, al igual esto:

val isOdd = ((_:Int) & 1) == 1

sin embargo esta vez el compilador se queja:

warning: comparing a fresh object using `==' will always yield false 
val isOdd = ((_:Int) & 1) == 1 

¿Qué significa esta advertencia? ¿por qué el compilador reconoce ((_ :Int) & 1) como un objeto nuevo en lugar de una operación en modo bit que da como resultado un valor? ¿hay alguna forma de escribir esta función literal usando la notación "_"?

+0

sólo tiene que utilizar: 'val = extraña! even (_: Int) ' –

Respuesta

20

El problema es, básicamente, que la Scala tiene que decir la diferencia entre

val isOdd = ((_:Int) & 1) == 1 

donde desea que todo a la derecha del signo igual sea un lambda, y

val result = collection.map(_ + 1) 

donde desea solamente la materia dentro de los paréntesis a ser un lambda

Scala ha decidido que cuando se utiliza el guión para crear una lambda, que va a recoger el paréntesis internos como los límites de ese lambda. Hay una excepción: (_:Int) no cuenta como el paréntesis más interno porque su propósito es solo agrupar el tipo de declaración con el marcador de posición _.

Por lo tanto:

val isOdd = ((_:Int) & 1) == 1 
      ^^^^^^^^^^^^^^ 
      this is the lambda 

val result = collection.map(_ + 1) 
          ^^^^^^^ 
          this is the lambda 

val result = collection.map((_ + 1)/2) 
          ^^^^^^^^ 
          this is the lambda 
          and the compiler can't infer the type of the _ 

val result = somemap.map((_ + 1)/2 * _) 
         ^^^^^^^^ 
         this is an inner lambda with one parameter 
         and the compiler can't infer the type of the _ 
         ^^^^^^^^^^^^^^^^^ 
         this is an outer lambda with one parameter 

Este último caso le permite hacer cosas como

_.map(_ + 1) 

y has que se traducen en

x => x.map(y=> y + 1) 
+1

+1 para un uso increíble de la tecla de intercalación (¡y una buena respuesta!) – Andy

+0

¡thx para aclarar las reglas sobre el límite de la expresión lambda! –

2

Lo Scala está haciendo es la siguiente:

  • se ve ((_:Int) & 1) y crea un objeto de tipo (Int) => Int, es decir, una función.
  • A continuación, aplica el operador de comparación == comparar esta función con el valor 1

Una función no es igual al valor 1. Por lo tanto el resultado es false, por lo que su código es equivalente a:

val isOdd = false 

Lo que podría hacer es crear otra función anónima que haga la parte == 1 de su cálculo. Esto es feo:

val isOdd = ((_: Int) & 1)(_: Int) == 1 

Esto es equivalente a la más prolija (y tal vez más fácil de entender):

val isOdd = (x: Int) => 1 == ((_: Int) & 1)(x) 
+5

No recomiendo ninguna de estas soluciones; ambas son más complicadas y ambas crean una lambda adicional e innecesaria. Quédate con 'val isOdd = (x: Int) => (x & 1) == 1' –

+1

¡Claro! Pero el OP preguntó específicamente si había alguna manera de que pudiera escribir su lambda "isOdd" usando solo "_". No creo que estemos discutiendo el buen estilo aquí. –

+0

Te perdiste 'val isOdd = ((_: ​​Int) & 1) y luego (_ == 1)'. :-) –

7

Hay que ir:

val isOdd = ((_: Int) & 1) andThen (1 ==) 
+0

+1 por la brillante idea de composición de funciones :) –

10

Sólo un poco trampa:

val isOdd = (_: Int) % 2 == 1 

:-)

+2

+1 No es hacer trampa. De hecho, mantengo que usar un '&' para esto es una optimización prematura. El formulario '%' es más claro, y cualquier compilador (o tal vez el JIT) valdrá la sal '% 2' y lo cambiará a' & 1' si es realmente más rápido en ese entorno operativo. –

+0

Acepto que, en general, deberíamos dejar este tipo de optimizaciones a los compiladores; pero aquí realmente quiero entender el razonamiento detrás de esta cosa lambda. de nuevo thx para aclarar la regla de paréntesis con respecto a lambda :) –

+0

@KenBloom Mantendría que usar el mejor algoritmo para un trabajo no es una optimización prematura, es solo una buena programación, y me canso de que las personas usen esta justificación para escribir código lento (en términos generales, este es un micro-ejemplo donde la diferencia es probablemente insignificante). Que puede lograrlo de forma más elegante con otro mecanismo tiene mucho que ver con el estilo y nada que ver con la optimización. – PlexQ

1

Un enfoque diferente

val isOdd = (_:Int).&(1) == 1 
Cuestiones relacionadas