Je comprends le rendement de Ruby et Python. Que fait le rendement de Scala?
Réponses
Trop de publicités?Je pense que l'on a accepté la réponse est grande, mais il semble que beaucoup de gens ont échoué à saisir quelques points fondamentaux.
Tout d'abord, Scala "pour les interprétations de la" équivalent à Haskell "faire" de la notation, et il n'est rien de plus qu'un sucre syntaxique pour la composition de plusieurs monadique opérations. Que cette déclaration ne sera probablement pas aider quelqu'un qui a besoin d'aide, essayez encore une fois... :-)
Scala "pour des compréhensions" est sucre syntaxique pour la composition de plusieurs opérations à la carte, flatMap et le filtre. Ou foreach. Scala en fait traduit une expression dans les appels à ces méthodes, de sorte que toute classe, ou un sous-ensemble d'entre eux, peuvent être utilisés pour des compréhensions.
Tout d'abord, nous allons parler des traductions. Il y a des règles très simples:
1) Cette
for(x <- c1; y <- c2; z <-c3) {...}
est traduit en
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
2) Ce
for(x <- c1; y <- c2; z <- c3) yield {...}
est traduit en
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
3) Cette
for(x <- c; if cond) yield {...}
se traduit sur Scala de 2,7 en
c.filter(x => cond).map(x => {...})
ou, sur Scala 2.8, en
c.withFilter(x => cond).map(x => {...})
avec un repli dans l'ancien si la méthode withFilter
n'est pas disponible, mais filter
. Consultez l'édition ci-dessous pour plus d'informations sur cette.
4) Ce
for(x <- c; y = ...) yield {...}
est traduit en
c.map(x => (x, ...)).map((x,y) => {...})
Quand vous regardez très simple pour des compréhensions, de la carte/foreach alternatives look, en effet, mieux. Une fois que vous commencez à composer eux, cependant, vous pouvez facilement se perdre dans les parenthèses et les niveaux d'imbrication. Lorsque cela se produit, pour des compréhensions sont généralement beaucoup plus claire.
Je vais vous montrer un exemple simple, et volontairement omettre aucune explication. Vous pouvez décider quelle syntaxe est 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
MODIFIER
Scala 2.8 introduit une méthode appelée withFilter
, dont la principale différence est que, au lieu de retourner un nouveau, de les filtrer, de collection, il les filtres sur la demande. L' filter
méthode a son comportement défini sur la base de la rigueur de la collection. Pour mieux comprendre ce processus, nous allons jeter un oeil à quelques Scala 2.7 List
(strict) et Stream
(non stricte):
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 le filtre est appliqué immédiatement avec List
, de retour d'une liste de cotes -- depuis found
est false
. Alors seulement, foreach
est exécuté, mais, par ce temps, en changeant found
est vide de sens, comme filter
a déjà été exécutée.
Dans le cas d' Stream
, la condition n'est pas immédiatement appliquées. Au lieu de cela, chaque élément est demandé par foreach
, filter
tests de l'état, qui permet aux foreach
de l'influencer par found
. Juste pour préciser, ici, c'est l'équivalent pour les-de la compréhension du code:
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é beaucoup de problèmes, parce que les gens devraient l' if
pour être considéré à la demande, au lieu d'être appliquée à l'ensemble de la collection à l'avance.
Scala 2.8 introduite withFilter
, ce qui est toujours non-stricte, peu importe la sévérité 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
Ce qui produit le résultat plupart des gens attendent, sans changer la façon dont filter
se comporte. Comme une note de côté, Range
a été modifié à partir de la non-strict strict entre Scala 2.7 et Scala 2.8.
Il est utilisé dans la séquence de compréhensions (comme le Python de la liste des inclusions et des générateurs, où vous pouvez utiliser yield
aussi).
Il est appliqué en combinaison avec d' for
, et a écrit un nouvel élément dans la séquence.
Exemple Simple (à partir de la 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
dans Linq.
Ruby yield
a un effet différent.
Oui, comme Earwicker dit, il est à peu près l'équivalent de LINQ select
et a très peu à voir avec Ruby et Python yield
. En gros, là où en C#, on peut écrire
from ... select ???
en Scala, vous les avez
for ... yield ???
Il est également important de comprendre que l' for
-compréhensions ne travaille pas seulement avec des séquences, mais avec n'importe quel type qui définit certaines méthodes, comme LINQ:
- Si votre type définit juste
map
, il permet d'for
-expressions composé d'un seul générateur. - Si elle définit
flatMap
ainsi quemap
, il permet d'for
-expressions composé de plusieurs générateurs. - Si elle définit
foreach
, il permet d'for
-boucles sans rendement (à la fois avec un seul ou de plusieurs générateurs). - Si elle définit
filter
, il permet d'for
-filtre expressions commençant avec uneif
dans l'for
expression.
À moins d'obtenir une meilleure réponse à partir d'un Scala utilisateur (dont je ne suis pas), voici ma compréhension.
Il n'apparaît que comme une partie 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
Donc il y a un élément de sortie pour chaque entrée (bien que je crois qu'il y a un moyen de déposer des doublons).
C'est très différent de la "impératif suites" activé par le rendement dans d'autres langues, où il fournit un moyen pour générer une liste de n'importe quelle longueur, à partir de quelques impératif de code avec presque n'importe quelle structure.
(Si vous êtes familier avec le C#, c'est plus proche de LINQ select
opérateur de elle est à l' yield return
).
Le mot-clé yield
en Scala est simplement du sucre syntaxique qui peut être facilement remplacé par un map
, comme Daniel Sobral déjà expliqué en détail.
D'autre part, yield
est tout à fait trompeuse si vous êtes à la recherche pour les générateurs (ou suites) similaires à ceux en Python. Voir ce fil pour plus d'informations: le moyen privilégié pour mettre en oeuvre une "rendement" de Scala?