2011-12-16 19 views
7

Vengo de Groovy y tiene un método .with en cada tipo que acepta un cierre de argumento único; el argumento es el objeto sobre el que se llama el método .with. Esto permite una técnica muy buena para extender las capacidades de encadenamiento funcional, lo que lo libera de la obligación de introducir variables temporales, factores su código, hace que sea más fácil de leer y hace otras sutilezas..with alternativa en scala

Quiero ser capaz de hacer algo como esto:

Seq(1, 2, 3, 4, 5) 
    .filter(_ % 2 == 0) 
    .with(it => if (!it.isEmpty) println(it)) 

En lugar de

val yetAnotherMeaninglessNameForTemporaryVariable = 
    Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) 
if (!yetAnotherMeaninglessNameForTemporaryVariable.isEmpty) 
    println(yetAnotherMeaninglessNameForTemporaryVariable) 

En otras palabras, en el primer ejemplo, el .with es un poco similar a .foreach pero en lugar de iterar a través los elementos del objeto al que se está llamando una vez en el objeto mismo. Entonces it es igual a Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).

Desde que era muy sorprendido no encontrar nada parecido en Scala, mis preguntas son:

  • me estoy perdiendo algo?
  • ¿hay alguna técnica alternativa nativa de Scala?
  • si no, ¿hay algún motivo de peso por el que esta característica no esté implementada en Scala?

Actualización: una solicitud de función apropiada ha sido publicado en el seguimiento de incidencias Scala: https://issues.scala-lang.org/browse/SI-5324. Por favor vote y promocione

+1

Solo una nota: 'con' es una palabra reservada en Scala, por lo que el método no podría nombrarse de la misma manera. Todavía debería existir bajo otro nombre; esta es la pregunta y respuesta más comunes de Scala en StackOverflow por lo que puedo decir ("no existe, haz tu propia así"). –

+0

Creo que el nombre 'convert' encajaría mejor, lo que sugiere que este método no debería tener efectos secundarios y, como toma la llamada como parámetro y devuelve algo nuevo, debe ser algún tipo de conversión. En ese sentido, esta función sería insustituible en la biblioteca estándar. También como se sugiere en http://stackoverflow.com/a/8538277/485115 también debe haber una variante de efecto lateral llamada 'tap', que devuelve el objeto llamante. –

Respuesta

12

No existe ningún método de este tipo en la biblioteca estándar, pero no es difícil definir el suyo.

implicit def aW[A](a: A) = new AW(a) 
class AW[A](a: A) { 
    def tap[U](f: A => U): A = { 
    f(a) 
    a 
    } 
} 

val seq = Seq(2, 3, 11). 
      map(_ * 3).tap(x => println("After mapping: " + x)). 
      filter(_ % 2 != 0).tap(x => println("After filtering: " + x)) 

EDIT: (en respuesta al comentario)

Oh, no he entendido bien. Lo que necesitas está allí en la biblioteca de Scalaz. Viene con el nombre |> (denominado operador de tuberías). Con eso, su ejemplo se vería como se muestra a continuación:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) |> { it => if(!it.isEmpty) println(it) } 

Si no puede utilizar Scalaz, se puede definir el operador en su propia:

implicit def aW[A](a: A) = new AW(a) 
class AW[A](a: A) { 
    def |>[B](f: A => B): B = f(a) 
} 

Y no es una mala práctica para chulo método útil (s) en tipos existentes.Debe usar conversiones implícitas con moderación, pero creo que estos dos combinadores son lo suficientemente comunes para que sus proxenetas sean justificables.

+0

Sí, sé que puedo extender la biblioteca estándar con implícitos, pero me parece que alejarme de los estándares es una mala práctica. Es por eso que mi pregunta fue por qué los chicos de Scala no implementan esto, porque probablemente esto debería publicarse en su rastreador de problemas. Además de su función 'tap' aunque muy útil es un poco diferente de' with' en el sentido de que en lugar del resultado del cierre devuelve el objeto en sí. Sería genial tener '.with' y' .tap' en la biblioteca estándar de la IMO. –

+0

Nombre el mismo método 'efecto'. ¿Por qué 'tocar'? Me gusta que sea más corto, pero me falta el beneficio nemotécnico. –

+3

@NikitaVolkov Odersky criticó el hábito de algunos programadores de Scala de evitar declaraciones 'val' intermedias a toda costa, lo que' |> 'ciertamente contribuye a, cuando se solicitó al operador de la tubería. Entonces, por ahora, no creo que se convierta en parte de la biblioteca estándar. –

1

Recuerde llamar por su nombre? Tal vez le da la capablity desea:

object Test { 
def main(args: Array[String]) { 
    delayed(time()); 
} 

def time() = { 
    println("Getting time in nano seconds") 
    System.nanoTime 
} 

def delayed(t: => Long) = { 
    println("In delayed method") 
    println("Param: " + t) 
    t 
} 
} 

como se describe en http://www.tutorialspoint.com/scala/functions_call_by_name.htm

3

algo Trate de esta manera.

println(Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).ensuring(!_.isEmpty)) 

Lanza una excepción de aserción si la condición no se cumple.

+0

+1, aunque solo funcionará para ejecuciones de instrucción única (por ejemplo, println como se cita aquí). si quisieras hacer más de una operación, se volvería complicado. – aishwarya

+0

Técnica agradable para otros fines. Para este lanzar una excepción es exagerado –

7

Existe cierta sintaxis para este patrón incluido en Scala:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) match { case it => if (!it.isEmpty) println(it) } 

Sin embargo, esto no es un idioma aceptado por lo que tal vez debe abstenerse de (ab) uso de ella.

Si no le gusta inventar y de montones de nombres para las variables ficticias, recuerde que puede utilizar alcance corchetes:

val importantResult = { 
    val it = Seq(1,2,3).filter(_ % 2 == 0) 
    if (!it.isEmpty) println(it) 
    it 
} 

val otherImportantResultWithASpeakingVariableName = { 
    val it = // ... 
    /* ... */ 
    it 
} 
+0

¡Buena sugerencia sobre el partido! Aunque es un poco duro, contesta las 2 primeras preguntas. –

1

Aunque me gusta otras soluciones mejores (ya que son más local y por lo tanto más fácil de seguir) , no olvide que puede

{ val x = Seq(1,2,3,4,5).filter(_ % 2 == 0); println(x); x } 

para evitar conflictos de nombres de las variables sin sentido y mantenerlos constreñido al ámbito apropiado.

1

Esto es sólo función de aplicación f(x) vuelta en su cabeza: x.with(f) ... Si usted está buscando una manera de hacer idiomática with en Scala, un-darle la vuelta:

(it => if (!it.isEmpty) println(it)) (Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0)) 

Del mismo modo, si usted quiere x.with(f).with(g), solo use g(f(x)) ...

+2

': 8: error: falta el tipo de parámetro' – david