2010-05-24 17 views
11

Por lo que yo entiendo, no hay manera en la Scala de tener múltiples puntos de retorno de una función anónima, es decirpuntos de retorno múltiples en Scala de cierre/función anónima

someList.map((i) => { 
    if (i%2 == 0) return i // the early return allows me to avoid the else clause 
    doMoreStuffAndReturnSomething(i) // thing of this being a few more ifs and returns 
}) 

plantea una error: return outside method definition. (Y si no fuera a aumentar que, el código no funciona como me gustaría que funcione.)

Una solución que pude cosa sería el siguiente

someList.map({ 
    def f(i: Int):Int = { 
     if (i%2 == 0) return i 
     doMoreStuffAndReturnSomething(i) 
    } 
    f 
}) 

sin embargo, Me gustaría saber si hay otra forma "aceptada" de hacer esto. Tal vez la posibilidad de ir sin un nombre para la función interna?

(Un caso de uso sería la de emular algunas valorada continue constructo dentro del bucle.)

Editar

Por favor, créeme, que hay una necesidad de evitar la declaración de otra persona, ya que, la doMoreStuff parte en realidad podría ser:

val j = someCalculation(i) 
if (j == 0) return 8 
val k = needForRecalculation(i) 
if (k == j) return 9 
finalRecalc(i) 
... 

el cual, cuando sólo tiene un if - estructura else disponibles obtiene correo asily en mal estado.

Por supuesto, en el ejemplo simple que di al comienzo, es más fácil simplemente usar else. Lo siento, pensé que esto estaba claro.

+0

¿Cuál es el problema al usar una instrucción else? – Patrick

+1

En el ejemplo que das, no hay ninguna razón para evitar la palabra clave 'else'; no se evalúa ninguna expresión extra si se usa 'else', por lo que no se gana nada si se usa un retorno anticipado aquí. – Jesper

+0

Lo siento, lo he revisado. Pensé que estaba claro que la parte 'doMoreStuff' en realidad * sí * un poco más. – Debilski

Respuesta

1

Creo que el principal problema con los puntos de retorno en las funciones anónimas es que una función anónima podría surgir en un lugar donde normalmente no se esperaría. Por lo tanto, no estaría claro a qué cierre pertenecería en realidad la declaración de devolución. Este problema se resuelve al requerir explícitamente una correspondencia def - return*.

Alternativamente, uno necesitaría guardias alrededor de la declaración para regresar. breakable - break pero desafortunadamente no puede devolver un valor con eso. Algunas soluciones basadas en la continuación podrían alcanzar ese objetivo, aunque me gustaría esperar algunas bibliotecas de aceptación general y allí.

5

Si su función anónima es tan compleja, la haría más explícita. Las funciones anónimas no son adecuadas para nada más complejo que unas pocas líneas. Se podría hacer que el método privada al declarar que en el método utilizando

def myF(i:Int):Int = { 
    if (i%2 == 0) return i 
    doMoreStuffAndReturnSomething(i) 
} 
someList.map(myF(_)) 

Esta es una variación en su solución, pero es más limpio. Ambos lo mantienen privado al alcance del método local.

+0

Esta es una gran solución. Soy un scala newb, así que realmente no entiendo por qué es necesario, pero resolvió el problema. – ripper234

3

En su comentario código, escribió que se quiere evitar la palabra clave else, pero en mi humilde opinión esto es exactamente lo que quiere y sus dos personajes incluso más corto ;-)

someList.map((i) => { 
    if (i%2 == 0) i else 
    doMoreStuffAndReturnSomething(i) 
}) 
+0

Por favor, mira mi edición. – Debilski

+0

En realidad, diría que ahorra más de dos caracteres: un 'return' explícito obliga a declarar el tipo de devolución de todos modos ... Sin embargo, mi caso de uso real sería un poco más complicado que el ejemplo que originalmente di, por lo que no es solo sobre guardar personajes – Debilski

3

El ejemplo que hemos dado es fácilmente resuelto por una declaración if. No hay rendimiento u otras penalidades por hacer esto.

Pero es posible que tenga alguna otra situación, que se ve más o menos así

if (test) { 
    if (anotherTest) { 
    val a = someComputation() 
    if (testOf(a)) return otherComputation() 
    } 
    else if (yetAnotherTest) return whatever() 
} 
bigComputation() 

Hay algunas maneras de hacer frente a este tipo de situación, si se quiere evitar el enredo de las sentencias if y/o código duplicación necesaria para convertir esto a un formulario sin devoluciones.

Hay varias razones que hacen que puede hacer con Option o Either para mantener el estado fluyendo a lo largo (con orElse y fold) de manera que lo hace sólo los cálculos que necesita.

Usted está realmente mejor creando una definición como usted sugiere.Pero sólo para comparar, considere un estilo Opción-envoltura:

i => { 
    (if ((i%2)==0) Some(i) 
    else None 
).getOrElse(doStuffAndReturn(i)) 
} 

En la gran ejemplo anterior, este estilo daría

(if (test) { 
    if (anotherTest) { 
     val a = someComputation() 
     if (testOf(a)) Some(otherComputation()) else None 
    } 
    else if (yetAnotherTest) Some(whatever()) 
    else None 
}).getOrElse(bigComputation()) 

Personalmente, no creo que es más clara (y ciertamente no es más rápido), pero es posible.

+0

Sí, es tan intrincado (o un poco más parejo) como el ejemplo original sin devoluciones. Pero, por supuesto, es un enfoque válido en otras situaciones. – Debilski

Cuestiones relacionadas