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 foret é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 yielda 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 forcompré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 forcompréhensions de Scala sont du sucre syntaxique pour la composition de plusieurs opérations avec la carte, flatMapet 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' filterest. 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 forcompré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 forcompré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
withFilterScala 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 filtermé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 filterest immédiatement appliquée avec List, retournant une liste des chances - depuis foundest false. Ce n'est qu'ensuite que foreachs'exécute, mais, à ce moment, le changement foundn'a plus de sens, comme cela filtera 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, filterteste la condition, ce qui permet foreachde 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 ifqu'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 Listavec 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 filtercomportement. En Rangepassant , a été changé de non strict à strict entre Scala 2.7 et Scala 2.8.
withFilterest 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 selectet 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 forexpressions composées d'un seul générateur.flatMapaussi 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 forexpressions -filter commençant par un if
dans l' forexpression.À 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é yielddans 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é, yieldest 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) iet 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.