Quel est le rendement de Scala?
Je comprends le rendement de Ruby et de Python. Que fait le rendement de Scala?
9 réponses
Il est utilisé dans sequence comprehensions (comme les list-comprehensions et les générateurs de Python, où vous pouvez également utiliser yield
).
Il est appliqué en combinaison avec for
et écrit un nouvel élément dans la séquence résultante.
Exemple Simple (à partir 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
Dans Linq.
Ruby yield
a un effet différent.
Je pense que la réponse acceptée est grande, mais il semble que beaucoup de gens ont échoué à saisir certains points fondamentaux.
Tout D'abord, les compréhensions for
de Scala sont équivalentes à la notation do
de Haskell, 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, essayons à nouveau...: -)
Les compréhensions de Scala for
sont des sucres syntaxiques pour la composition de plusieurs opérations avec la carte, flatMap
et filter
. Ou foreach
. Scala traduit en fait une expression for
en appels à ces méthodes, de sorte que toute classe qui les fournit, ou un sous-ensemble d'entre eux, peut être utilisée pour les compréhensions.
Parlons D'abord des traductions. Il y a 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, dans
c.withFilter(x => cond).map(x => {...})
Avec un repli dans l'ancienne méthode if
withFilter
n'est pas disponible maisfilter
l'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 compréhensions très simples for
, le map
/foreach
les alternatives semblent, en effet, mieux. Une fois que vous commencez à les composer, cependant, vous pouvez facilement obtenir perdu entre parenthèses et les niveaux d'imbrication. Lorsque cela se produit, for
compréhensions sont généralement beaucoup plus claires.
Je vais montrer un exemple simple, et intentionnellement omettre 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 que, au lieu de renvoyer une nouvelle collection filtrée, elle filtre à la demande. La méthode filter
a son comportement défini en fonction de la rigueur de la collection. Pour mieux comprendre cela, jetons un coup D'oeil à 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é avec List
, renvoyant une liste de cotes-puisque found
est false
. Seulement alors foreach
est exécuté, mais, à ce moment-là, changer found
n'a pas de sens, comme filter
l'a déjà exécuté.
Dans le cas de Stream
, la condition n'est pas immédiatement appliquées. Au lieu de cela, comme chaque élément est demandé par foreach
, filter
teste la condition, ce qui permet à foreach
de l'influencer à travers found
. Juste pour être clair, voici l'équivalent pour le code 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é beaucoup de problèmes, car les gens s'attendaient à ce que le if
soit considéré à la demande, au lieu d'être appliqué à l'ensemble de la collection au préalable.
Scala 2.8 introduit withFilter
, 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
Cela produit le résultat que la plupart des gens attendent, sans changer le comportement de filter
. Comme note de côté, Range
a été changé de non-strict à strict entre Scala 2.7 et Scala 2.8.
Oui, comme l'a dit Earwicker, c'est à peu près l'équivalent de select
de LINQ et a très peu à voir avec yield
de Ruby et de Python. Fondamentalement, où en C# vous écririez
from ... select ???
Dans Scala vous avez à la place
for ... yield ???
Il est également important de comprendre que for
- compréhensions ne fonctionnent pas seulement avec des séquences, mais avec tout type qui définit certaines méthodes, tout comme LINQ:
- Si votre type ne définit que
map
, il autorisefor
- des expressions composées de un seul générateur. - , Si elle définit
flatMap
ainsi quemap
, il permet defor
-expressions composé de plusieurs générateurs. - s'il définit
foreach
, il permetfor
- boucles sans rendement (à la fois avec des générateurs simples et multiples). - s'il définit
filter
, il autorisefor
- filtrer les expressions commençant par unif
dans l'expressionfor
.
Le mot-clé yield
dans Scala est simplement du sucre syntaxique, qui peut être facilement remplacé par un map
, comme Daniel Sobral déjà expliqué dans le détail.
D'autre part, yield
est absolument trompeur si vous recherchez des générateurs (ou des continuations) similaires à ceux de Python . Voir ce thread SO Pour plus d'Informations: Quel est le moyen préféré d'implémenter 'yield' dans Scala?
Sauf si vous obtenez une meilleure réponse d'un utilisateur Scala (ce que 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
Il y a donc un élément de sortie pour chaque entrée (bien que je crois qu'il existe un moyen de supprimer les doublons).
Ceci est tout à fait différent des "continuations impératives" activées par yield dans d'autres langues, 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 êtes familier avec C#, il est plus proche de LINQ select
opérateur que c'est à yield return
).
Considérez ce qui suit pour-compréhension
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
, if, il est plus grand que 3
, puis rendement (produire) i
et l'ajouter à la liste A
."
En termes de notation mathématique set-builder , LA for-compréhension ci-dessus est analogue à
Qui peut être interprété comme
"Pour chaque entier , si, il est supérieur , alors il est un membre de l'ensemble ."
Ou alternativement comme
" est l'ensemble de tous les entiers, tels que chacun est supérieur à ."
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 éléments 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 éléments de code sont également équivalents.
La carte est aussi flexible que le rendement et vice-versa.
Yield est similaire à For loop 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 se termine, elle renvoie la collection de toutes les valeurs générées. Yield peut être utilisé comme opérateurs arithmétiques simples 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.immuable.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)] = List((1,a), (1,b), (1,c), (2,a), (2,b), (2,c), (3,a), (3,b), (3,c))
Espérons que cela aide!!
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 )
Yield affichera le résultat comme: List (5, 6), ce qui est bon
While map() retournera le résultat comme: List(false, false, true, true, true), ce qui n'est probablement pas ce que vous avez l'intention.