Je comprends le rendement de Ruby et Python. Que fait le rendement de Scala?
312
Je comprends le rendement de Ruby et Python. Que fait le rendement de Scala?
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
etfilter
. Ouforeach
. Scala traduit en fait unefor
-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
est traduit en
Ce
est traduit en
Ce
est traduit sur Scala 2.7 en
ou, sur Scala 2.8, en
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
est traduit en
Lorsque vous regardez des
for
compréhensions très simples , lemap
/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, lesfor
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.
ou
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. Lafilter
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 avecList
(strict) etStream
(non strict):La différence se produit parce que
filter
est immédiatement appliquée avecList
, retournant une liste des chances - depuisfound
estfalse
. Ce n'est qu'ensuite queforeach
s'exécute, mais, à ce moment, le changementfound
n'a plus de sens, comme celafilter
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é parforeach
,filter
teste la condition, ce qui permetforeach
de l'influencerfound
. Juste pour être clair, voici le code équivalent de compréhension: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 montreList
avec les deux méthodes sur Scala 2.8:Cela produit le résultat que la plupart des gens attendent, sans changer le
filter
comportement. EnRange
passant , a été changé de non strict à strict entre Scala 2.7 et Scala 2.8.la source
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 enc.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 enc.map(x => (x, ...)).map((x,y) => {...})
? Je pense que c'est traduitc.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 Pythonyield
. Fondamentalement, où en C # vous écririezà Scala, vous avez à la place
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 desfor
expressions composées d'un seul générateur.flatMap
aussi bien quemap
, il permetfor
expressions composées de plusieurs générateurs.foreach
, il autorisefor
-loops sans rendement (à la fois avec des générateurs simples et multiples).filter
, il autorise lesfor
expressions -filter commençant par unif
dans l'for
expression.la source
À 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:
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'estyield return
).la source
Le mot clé
yield
dans Scala est simplement du sucre syntaxique qui peut être facilement remplacé par unmap
, 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?la source
Tenez compte de la for-comprehension suivante
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 listeA
."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 à ."
la source
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
res: scala.collection.immutable.IndexedSeq [Int] = Vecteur (3, 6, 9, 12, 15)
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!!
la source
Ces deux morceaux de code sont équivalents.
Ces deux morceaux de code sont également équivalents.
La carte est aussi flexible que le rendement et vice-versa.
la source
le rendement est plus flexible que map (), voir l'exemple ci-dessous
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.
la source