Réponses:
Il est utilisé dans les compréhensions de séquences (comme les compréhensions de liste et les générateurs de Python, où vous pouvez yield
également les utiliser ).
Il est appliqué en combinaison avec for
et écrit un nouvel élément dans la séquence résultante.
Exemple simple (de scala-lang )
/** Turn command line arguments to uppercase */
object Main {
def main(args: Array[String]) {
val res = for (a <- args) yield a.toUpperCase
println("Arguments: " + res.toString)
}
}
L'expression correspondante en F # serait
[ for a in args -> a.toUpperCase ]
ou
from a in args select a.toUpperCase
à Linq.
Ruby yield
a un effet différent.
Je pense que la réponse acceptée est excellente, mais il semble que beaucoup de gens n'aient pas saisi certains points fondamentaux.
Tout d'abord, les for
compréhensions de Scala sont équivalentes à celles de Haskelldo
notation , et ce n'est rien de plus qu'un sucre syntaxique pour la composition de multiples opérations monadiques. Comme cette déclaration n'aidera probablement personne qui a besoin d'aide, réessayons… :-)
Les for
compréhensions de Scala sont du sucre syntaxique pour la composition de plusieurs opérations avec la carte, flatMap
et filter
. Ou foreach
. Scala traduit en fait une for
-expression en appels à ces méthodes, de sorte que toute classe qui les fournit, ou un sous-ensemble d'entre elles, peut être utilisée pour des compréhensions.
Parlons d'abord des traductions. Il existe des règles très simples:
Ce
for(x <- c1; y <- c2; z <-c3) {...}
est traduit en
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
Ce
for(x <- c1; y <- c2; z <- c3) yield {...}
est traduit en
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
Ce
for(x <- c; if cond) yield {...}
est traduit sur Scala 2.7 en
c.filter(x => cond).map(x => {...})
ou, sur Scala 2.8, en
c.withFilter(x => cond).map(x => {...})
avec un repli sur l'ancienne méthode if withFilter
n'est pas disponible mais l' filter
est. Veuillez consulter la section ci-dessous pour plus d'informations à ce sujet.
Ce
for(x <- c; y = ...) yield {...}
est traduit en
c.map(x => (x, ...)).map((x,y) => {...})
Lorsque vous regardez des for
compréhensions très simples , le map
/foreach
alternatives semblent meilleures. Une fois que vous commencez à les composer, vous pouvez facilement vous perdre dans les parenthèses et les niveaux d'imbrication. Lorsque cela se produit, les for
compréhensions sont généralement beaucoup plus claires.
Je vais montrer un exemple simple et omettre intentionnellement toute explication. Vous pouvez décider quelle syntaxe était plus facile à comprendre.
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
ou
for {
sl <- l
el <- sl
if el > 0
} yield el.toString.length
withFilter
Scala 2.8 a introduit une méthode appelée withFilter
, dont la principale différence est qu'au lieu de renvoyer une nouvelle collection filtrée, elle filtre à la demande. La filter
méthode a son comportement défini en fonction de la rigueur de la collection. Pour mieux comprendre cela, jetons un coup d'œil à certains Scala 2.7 avec List
(strict) et Stream
(non strict):
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
La différence se produit parce que filter
est immédiatement appliquée avec List
, retournant une liste des chances - depuis found
est false
. Ce n'est qu'ensuite que foreach
s'exécute, mais, à ce moment, le changement found
n'a plus de sens, comme cela filter
a déjà été exécuté.
Dans le cas de Stream
, la condition n'est pas appliquée immédiatement. Au lieu de cela, comme chaque élément est demandé par foreach
, filter
teste la condition, ce qui permet foreach
de l'influencer found
. Juste pour être clair, voici le code équivalent de compréhension:
for (x <- List.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
for (x <- Stream.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
Cela a causé de nombreux problèmes, car les gens s'attendaient à ce if
qu'ils soient considérés à la demande, au lieu d'être appliqués à l'ensemble de la collection au préalable.
Introduction de Scala 2.8 withFilter
, qui est toujours non stricte, quelle que soit la rigueur de la collection. L'exemple suivant montre List
avec les deux méthodes sur Scala 2.8:
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
Cela produit le résultat que la plupart des gens attendent, sans changer le filter
comportement. En Range
passant , a été changé de non strict à strict entre Scala 2.7 et Scala 2.8.
withFilter
est censé être non strict également, même pour les collections strictes, ce qui mérite quelques explications. Je vais considérer cela ...
for(x <- c; y <- x; z <-y) {...}
est traduit en c.foreach(x => x.foreach(y => y.foreach(z => {...})))
2. for(x <- c; y <- x; z <- y) yield {...}
est traduit enc.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}
vraiment traduit en c.map(x => (x, ...)).map((x,y) => {...})
? Je pense que c'est traduit c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})
ou il me manque quelque chose?
Oui, comme l'a dit Earwicker, c'est à peu près l'équivalent de LINQ select
et a très peu à voir avec Ruby et Python yield
. Fondamentalement, où en C # vous écririez
from ... select ???
à Scala, vous avez à la place
for ... yield ???
Il est également important de comprendre que les for
-compréhensions ne fonctionnent pas seulement avec des séquences, mais avec tout type qui définit certaines méthodes, tout comme LINQ:
map
, il autorise des for
expressions composées d'un seul générateur.flatMap
aussi bien que map
, il permetfor
expressions composées de plusieurs générateurs.foreach
, il autorise for
-loops sans rendement (à la fois avec des générateurs simples et multiples).filter
, il autorise les for
expressions -filter commençant par un if
dans l' for
expression.À moins que vous n'obteniez une meilleure réponse d'un utilisateur de Scala (ce que je ne suis pas), voici ma compréhension.
Il n'apparaît que dans le cadre d'une expression commençant par for
, qui indique comment générer une nouvelle liste à partir d'une liste existante.
Quelque chose comme:
var doubled = for (n <- original) yield n * 2
Il y a donc un élément de sortie pour chaque entrée (bien que je pense qu'il existe un moyen de supprimer les doublons).
Ceci est très différent des «continuations impératives» activées par yield dans d'autres langages, où il fournit un moyen de générer une liste de n'importe quelle longueur, à partir d'un code impératif avec presque n'importe quelle structure.
(Si vous connaissez C #, il est plus proche de l' select
opérateur de LINQ qu'il ne l'est yield return
).
Le mot clé yield
dans Scala est simplement du sucre syntaxique qui peut être facilement remplacé par un map
, comme Daniel Sobral l'a déjà expliqué en détail.
D'un autre côté, yield
est absolument trompeur si vous recherchez des générateurs (ou des continuations) similaires à ceux de Python . Voir ce fil SO pour plus d'informations: Quelle est la façon préférée d'implémenter le «rendement» dans Scala?
Tenez compte de la for-comprehension suivante
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Il peut être utile de le lire à haute voix comme suit
" Pour chaque entier i
, s'il est supérieur à 3
, alors céder (produire) i
et l'ajouter à la liste A
."
En termes de notation mathématique de constructeur d’ensemble , la compréhension ci-dessus est analogue à
qui peut être lu comme
" Pour chaque entier , s'il est supérieur à , alors c'est un membre de l'ensemble ."
ou bien comme
" est l'ensemble de tous les entiers , tels que chacun est supérieur à ."
Le rendement est similaire à la boucle for qui a un tampon que nous ne pouvons pas voir et pour chaque incrément, il continue d'ajouter l'élément suivant au tampon. Lorsque la boucle for termine son exécution, elle renvoie la collection de toutes les valeurs générées. Le rendement peut être utilisé comme de simples opérateurs arithmétiques ou même en combinaison avec des tableaux. Voici deux exemples simples pour une meilleure compréhension
scala>for (i <- 1 to 5) yield i * 3
res: scala.collection.immutable.IndexedSeq [Int] = Vecteur (3, 6, 9, 12, 15)
scala> val nums = Seq(1,2,3)
nums: Seq[Int] = List(1, 2, 3)
scala> val letters = Seq('a', 'b', 'c')
letters: Seq[Char] = List(a, b, c)
scala> val res = for {
| n <- nums
| c <- letters
| } yield (n, c)
res: Seq [(Int, Char)] = Liste ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), (( 3, a), (3, b), (3, c))
J'espère que cela t'aides!!
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.filter(_ > 3).map(_ + 1)
println( res3 )
println( res4 )
Ces deux morceaux de code sont équivalents.
val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
Ces deux morceaux de code sont également équivalents.
La carte est aussi flexible que le rendement et vice-versa.
le rendement est plus flexible que map (), voir l'exemple ci-dessous
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
le rendement affichera le résultat comme: Liste (5, 6), ce qui est bon
tandis que map () retournera un résultat comme: List (false, false, true, true, true), ce qui n'est probablement pas ce que vous voulez.