2010-05-24 11 views
11

Per quanto mi risulta, non c'è modo a Scala di avere più punti di ritorno in una funzione anonima, cioèpunti di ritorno multipli in scala di chiusura/funzione anonima

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 
}) 

solleva un error: return outside method definition. (E se non fosse per sollevare questo, il codice sarebbe non funziona come mi piacerebbe farlo funzionare.)

Una soluzione ho potuto cosa sarebbero i seguenti

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

però, Mi piacerebbe sapere se c'è un altro modo "accettato" di farlo. Forse una possibilità di andare senza un nome per la funzione interiore?

(Un caso d'uso potrebbe essere quello di emulare un certo valore continue costrutto all'interno del ciclo.)

Modifica

prego di credermi, che v'è la necessità di evitare la dichiarazione altro, perché, il doMoreStuff parte potrebbe effettivamente apparire come:

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

che, quando si ha un solo if - struttura else disponibili ottiene e facilmente incasinato.

Naturalmente, nel semplice esempio che ho dato all'inizio, è più semplice usare solo else. Scusa, ho pensato che fosse chiaro.

+0

Qual è il problema utilizzando un'altra istruzione? – Patrick

+1

Nell'esempio fornito non vi è alcun motivo per evitare la parola chiave 'else'; non viene valutata alcuna espressione extra se si usa 'else', quindi non si guadagna nulla utilizzando un ritorno anticipato qui. – Jesper

+0

Scusa, l'ho rivisto. Pensavo fosse chiaro che la parte 'doMoreStuff' in realtà * fa * un po 'di più. – Debilski

risposta

1

Penso che il problema principale con i punti di ritorno nelle funzioni anonime sia che una funzione anonima potrebbe sorgere in un luogo dove normalmente non ci si aspetterebbe. Quindi, non sarebbe chiaro a quale chiusura l'affermazione di reso sarebbe effettivamente appartenuta. Questo problema viene risolto richiedendo esplicitamente una corrispondenza def - return*.

In alternativa, uno avrebbe bisogno di guardie attorno alla dichiarazione da cui tornare. breakable - break ma sfortunatamente non è possibile restituire un valore con quello. Alcune soluzioni basate sulla continuazione sarebbero in grado di raggiungere questo obiettivo, anche se mi piacerebbe aspettare un po 'di accettazione generale e librerie lì.

5

Se la tua funzione anonima è così complessa, la renderei più esplicita. Le funzioni anonime non sono adatte a qualcosa di più complesso di poche righe. Si potrebbe rendere il metodo che privata dichiarando all'interno del usando il metodo

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

Questa è una variante sulla vostra soluzione, ma è più pulita. Entrambi lo mantengono privato nell'ambito del metodo locale.

+0

Questa è una soluzione eccezionale. Sono una scala newb quindi non capisco perché è necessario, ma ha risolto il problema. – ripper234

3

Nel tuo commento codice, è scritto che si vuole evitare la parola chiave else, ma secondo me questo fa esattamente quello che vuoi e le sue anche due personaggi più breve ;-)

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

Vedi la mia modifica. – Debilski

+0

In realtà, direi che salva più di due caratteri: un esplicito 'return' ti obbliga a dichiarare il tipo di ritorno comunque ... Comunque il mio caso d'uso effettivo sarebbe un po 'più complicato rispetto all'esempio che ho dato in origine, quindi non è quasi a salvare i personaggi. – Debilski

3

L'esempio che hai dato è facilmente risolto da una dichiarazione if. Non ci sono prestazioni o altre penalità per farlo.

Ma si potrebbe avere qualche altra situazione, che si presenta più o meno come

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

ci sono alcuni modi per affrontare questo tipo di situazione, se si vuole evitare il groviglio di se-dichiarazioni e/o il codice duplicazione necessaria per convertire questo in un modulo senza resi.

Ci sono varie cose che si possono fare subdolo con Option o Either per mantenere lo stato che scorre lungo (con orElse e fold) in modo che si fa solo i calcoli è necessario.

È molto meglio creare una def come suggerisci.Ma solo per confrontare, prendere in considerazione uno stile Opzione-wrapping:

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

Sulla grande esempio di cui sopra, questo stile darebbe

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

Personalmente, non credo che sia più chiaro (ed è certamente non più veloce), ma è possibile.

+0

Sì, è così contorto (o leggermente più uniforme) come l'esempio originale senza ritorno. Ma ovviamente è un approccio valido in altre situazioni. – Debilski