Quel est le rendement de Scala?

Je comprends le rendement de Ruby et Python. Que fait le rendement de Scala?

Il est utilisé dans la compréhension des séquences (comme les listes de compréhension et les générateurs de Python, où vous pouvez également utiliser le yield ).

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[Ssortingng]) { 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.

Le yield de Ruby a un effet différent.

Je pense que la réponse acceptée est excellente, mais il semble que beaucoup de gens n’ont pas compris certains points fondamentaux.

Premièrement, les scala for compréhension sont équivalentes à la notation do de Haskell, et ce n’est rien de plus qu’un sucre syntaxique pour la composition de plusieurs opérations monadiques. Comme cette déclaration n’aidera probablement personne qui a besoin d’aide, essayons encore une fois 🙂

Scala for compréhension est le sucre syntaxique pour la composition de plusieurs opérations avec map, flatMap et filter . Ou foreach . Scala traduit en réalité une expression pour les appeler à ces méthodes, de sorte que toute classe qui les fournit, ou un sous-ensemble de ces méthodes, peut être utilisée pour comprendre.

Parlons d’abord des traductions. Il y a des règles très simples:

  1. Ce

     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. 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'ancien si la méthode withFilter n'est pas disponible mais que le filter est. Veuillez consulter la section ci-dessous pour plus d'informations à ce sujet.

  4. Ce

     for(x <- c; y = ...) yield {...} 

    est traduit en

     c.map(x => (x, ...)).map((x,y) => {...}) 

Lorsque vous examinez très simplement les compréhensions, les alternatives map / foreach semblent en effet meilleures. Une fois que vous commencez à les composer, vous pouvez facilement vous perdre entre parenthèses et en niveaux de nidification. Lorsque cela se produit, les compréhensions sont généralement beaucoup plus claires.

Je vais montrer un exemple simple et omettre intentionnellement une explication. Vous pouvez décider quelle syntaxe était plus facile à comprendre.

 l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toSsortingng.length)) 

ou

 for { sl <- l el <- sl if el > 0 } yield el.toSsortingng.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 de filter a son comportement défini en fonction de la rigueur de la collection. Pour mieux comprendre cela, examinons quelques Scala 2.7 avec List (ssortingct) et Stream (non-ssortingct):

 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 est due au fait que le filter est immédiatement appliqué à la List , renvoyant une liste de cotes - car found false . Ce n'est foreach est exécuté, mais, à ce moment, le changement found n'a pas de sens, car le 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 , le filter teste la condition, ce qui permet à chaque élément d'influencer via la fonction found . Pour que ce soit clair, voici l'équivalent du 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é de nombreux problèmes, car les gens s'attendaient à ce qu'ils soient considérés à la demande, au lieu d'être appliqués à l'ensemble de la collection au préalable.

Scala 2.8 introduit withFilter , qui est toujours non ssortingct, peu importe 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 auquel la plupart des gens s'attendent, sans changer le comportement du filter . En parallèle, Range est passé de non ssortingct à ssortingct entre Scala 2.7 et Scala 2.8.

Oui, comme l’a dit Earwicker, c’est à peu près l’équivalent de la select de LINQ et cela a très peu à voir avec le yield Ruby et Python. En gros, où en C # vous écririez

 from ... select ??? 

à Scala, vous avez plutôt

 for ... yield ??? 

Il est également important de comprendre que for -comprehensions, il ne suffit pas de travailler avec des séquences, mais avec n’importe quel type qui définit certaines méthodes, tout comme LINQ:

  • Si votre type ne définit que la map , il permet for expressions consistant en un seul générateur.
  • Si elle définit flatMap aussi bien que map , cela permet for expressions composées de plusieurs générateurs.
  • S’il définit foreach , il permet for boucles sans rendement (à la fois avec des générateurs simples et multiples).
  • S’il définit le filter , il permet for expressions de filter commençant par if dans l’expression for .

Le mot clé en Scala est simplement un sucre syntaxique qui peut être facilement remplacé par une map , comme l’a déjà expliqué en détail Daniel Sobral .

D’un autre côté, le yield est absolument trompeur si vous recherchez des générateurs (ou des suites) similaires à ceux de Python . Voir ce thread SO pour plus d’informations: Quelle est la méthode préférée pour implémenter le «rendement» 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 apparaît uniquement 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 le rendement 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' opérateur select LINQ que du yield return ).

Considérez ce qui suit pour la 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 nombre entier i , s’il est supérieur à 3 , cédez (produisez) i et ajoutez-le à la liste A “.

En ce qui concerne la notation mathématique des constructeurs , ce qui précède pour la compréhension est analogue à

set-notation

qui peut être lu comme

Pour chaque entier je , si elle est supérieure à 3 , alors c’est un membre de l’ensemble UNE . “

ou bien comme

UNE est l’ensemble de tous les entiers je , tel que chacun je est supérieur à 3 . “

 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 similaire à celui de la boucle qui a un tampon que nous ne pouvons pas voir et pour chaque incrément, il ajoute continuellement l’élément suivant au tampon. Lorsque la boucle for se termine, elle renvoie la collection de toutes les valeurs générées. Le rendement peut être utilisé comme simple opérateur arithmétique ou même en combinaison avec des tableaux. Voici deux exemples simples pour mieux comprendre

 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)] = List ((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!!

le rendement est plus souple que map (), voir 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 imprimera le résultat comme: Liste (5, 6), qui est bon

while map () renverra un résultat comme: List (false, false, true, true, true), ce qui n’est probablement pas ce que vous avez l’intention de faire.