2010-05-24 9 views
11

Pour autant que je le comprends, il n'y a aucun moyen de Scala d'avoir plusieurs points de retour dans une fonction anonyme, à savoirpoints de retour multiples dans la fermeture scala/fonction anonyme

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

soulève une error: return outside method definition. (Et si elle était de ne pas soulever que, le code ne fonctionnerait pas comme je voudrais que cela fonctionne.)

Une solution que je pouvais chose serait la suivante

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

cependant, je J'aimerais savoir s'il existe une autre façon «acceptée» de le faire. Peut-être une possibilité d'aller sans un nom pour la fonction interne?

(Un cas d'utilisation serait d'imiter une continue construction d'une valeur dans la boucle.)

Modifier

S'il vous plaît, croyez-moi, qu'il est nécessaire d'éviter l'instruction else, parce que, le doMoreStuff partie pourrait effectivement ressembler à:

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

qui, lorsque vous avez seulement une if - la structure disponible obtient else e Asily foiré.

Bien sûr, dans l'exemple simple que j'ai donné au début, il est plus facile de simplement utiliser else. Désolé, je pensais que c'était clair.

+0

Quel est le problème en utilisant une instruction else? – Patrick

+1

Dans l'exemple que vous donnez, il n'y a aucune raison d'éviter le mot-clé 'else'; il n'y a pas d'expression supplémentaire en cours d'évaluation si vous utilisez 'else', donc vous ne gagnez rien en utilisant un retour anticipé ici. – Jesper

+0

Désolé, je l'ai révisé. Je pensais qu'il était clair que la partie 'doMoreStuff' fait * fait * un peu plus. – Debilski

Répondre

1

Je pense que le principal problème avec les points de retour dans les fonctions anonymes est qu'une fonction anonyme pourrait surgir à un endroit où normalement on ne s'y attendrait pas. Ainsi, il ne serait pas clair à quelle fermeture l'énoncé de retour appartiendrait réellement. Ce problème est résolu en exigeant explicitement une correspondance def - return*.

Alternativement, il faudrait des gardes autour de l'instruction à partir de laquelle retourner. breakable - break mais malheureusement ne peut pas retourner une valeur avec ça. Certaines solutions basées sur la continuité seraient en mesure d'atteindre cet objectif, bien que je voudrais attendre une acceptation générale et des bibliothèques là-bas.

5

Si votre fonction anonyme est complexe, je la rendrais plus explicite. Les fonctions anonymes ne sont pas adaptées à quelque chose de plus complexe que quelques lignes. Vous pouvez faire méthode privée en le déclarant dans la méthode utilisant

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

Ceci est une variante de votre solution de contournement, mais est plus propre. Ils les gardent tous les deux privés à la portée de la méthode locale.

+0

Ceci est une bonne solution de contournement. Je suis un scala newb, donc je ne comprends pas vraiment pourquoi c'est nécessaire, mais cela a résolu le problème. – ripper234

3

Dans votre commentaire de code, vous avez écrit que vous voulez éviter le mot-clé else, mais à mon humble avis ce fait exactement ce que vous voulez et de ses deux personnages, même plus court ;-)

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

S'il vous plaît voir mon edit. – Debilski

+0

En fait, je dirais qu'il enregistre plus de deux caractères: Un 'return' explicite vous oblige à déclarer le type de retour de toute façon ... Cependant, mon cas d'utilisation serait un peu plus compliqué que l'exemple précédent. à peu près sauver des personnages. – Debilski

3

L'exemple que vous avez donné est facilement résolu par une instruction if. Il n'y a pas de performance ou d'autres pénalités pour cela.

Mais vous pourriez avoir une autre situation, qui ressemble à peu près comme

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

Il y a quelques façons de faire face à ce genre de situation, si vous voulez éviter l'enchevêtrement des instructions if et/ou le code duplication nécessaire pour convertir cela en un formulaire sans retours.

Il y a plusieurs choses sournoises que vous pouvez faire avec Option ou Either pour maintenir l'état circulant le long (avec orElse et fold) afin que vous faites que les calculs que vous avez besoin.

Vous êtes vraiment mieux de créer un def comme vous le suggérez.Mais juste pour comparer, envisager un style Option d'emballage:

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

Sur le grand exemple ci-dessus, ce style donnerait

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

Personnellement, je ne pense pas que ce soit plus clair (et il est certainement pas plus rapide), mais c'est possible.

+0

Ouais, c'est aussi compliqué (ou légèrement plus pair) que l'exemple original sans retours. Mais bien sûr, c'est une approche valable dans d'autres situations. – Debilski

Questions connexes