Je lis le livre de recettes Scala ( http://shop.oreilly.com/product/0636920026914.do )
Il y a un exemple lié à l'utilisation future qui implique la compréhension.
Jusqu'à présent, ma compréhension de la compréhension est lorsque l'utilisation avec une collection produira une autre collection du même type. Par exemple, si chaque futureX
est de type Future[Int]
, les éléments suivants doivent également être de type Future[Int]
:
for {
r1 <- future1
r2 <- future2
r3 <- future3
} yield (r1+r2+r3)
Quelqu'un pourrait-il m'expliquer ce qui se passe exactement lorsque <-
est utilisé dans ce code? Je sais que s'il s'agissait d'un générateur, il récupérera chaque élément en boucle.
D'abord pour comprendre. Le SO a répondu à maintes reprises qu'il s'agissait d'une abstraction sur deux opérations monadiques: map
, flatMap
, withFilter
. Lorsque vous utilisez <-
, scalac désugare ces lignes en monadique flatMap
:
r <- monad
dans monad.flatMap(r => ... )
cela ressemble à un calcul impératif (en quoi consiste une monade), vous liez un résultat de calcul à r
. Et la partie yield
est décomposée en appel map
. Le type de résultat dépend du type de monad
's.
Le trait Future
a une fonction flatMap
et map
, nous pouvons donc l'utiliser pour la compréhension. Dans votre exemple, vous pouvez utiliser le code suivant:
future1.flatMap(r1 => future2.flatMap(r2 => future3.map(r3 => r1 + r2 + r3) ) )
Il va sans dire que si l'exécution de future2
dépend de r1
, vous ne pouvez pas échapper à l'exécution séquentielle, mais si les calculs futurs sont indépendants, vous avez le choix. Vous pouvez appliquer une exécution séquentielle ou allow pour une exécution parallèle. Vous ne pouvez pas appliquer ce dernier, car le contexte d'exécution le gérera.
val res = for {
r1 <- computationReturningFuture1(...)
r2 <- computationReturningFuture2(...)
r3 <- computationReturningFuture3(...)
} yield (r1+r2+r3)
sera toujours exécuté de manière séquentielle. Cela peut être facilement expliqué par le desugaring, après quoi les appels computationReturningFutureX
suivants sont uniquement appelés à l'intérieur des flatMaps, c'est-à-dire.
computationReturningFuture1(...).flatMap(r1 =>
computationReturningFuture2(...).flatMap(r2 =>
computationReturningFuture3(...).map(r3 => r1 + r2 + r3) ) )
Cependant, cela peut fonctionner en parallèle et pour la compréhension agrège les résultats:
val future1 = computationReturningFuture1(...)
val future2 = computationReturningFuture2(...)
val future3 = computationReturningFuture3(...)
val res = for {
r1 <- future1
r2 <- future2
r3 <- future3
} yield (r1+r2+r3)
Pour élaborer ces réponses existantes, voici un résultat simple montrant comment fonctionne la compréhension for
.
Ses fonctions sont un peu longues, mais elles valent la peine d’être examinées.
Une fonction qui nous donne une plage d'entiers
scala> def createIntegers = Future{
println("INT "+ Thread.currentThread().getName+" Begin.")
val returnValue = List.range(1, 256)
println("INT "+ Thread.currentThread().getName+" End.")
returnValue
}
createIntegers: createIntegers: scala.concurrent.Future[List[Int]]
Une fonction qui nous donne une gamme de caractères
scala> def createAsciiChars = Future{
println("CHAR "+ Thread.currentThread().getName+" Begin.")
val returnValue = new ListBuffer[Char]
for (i <- 1 to 256){
returnValue += i.toChar
}
println("CHAR "+ Thread.currentThread().getName+" End.")
returnValue
}
createAsciiChars: scala.concurrent.Future[scala.collection.mutable.ListBuffer[Char]]
Utiliser ces appels de fonction dans le pour la compréhension.
scala> val result = for{
i <- createIntegers
s <- createAsciiChars
} yield i.Zip(s)
Await.result(result, Duration.Inf)
result: scala.concurrent.Future[List[(Int, Char)]] = Future(<not completed>)
Pour les lignes ci-dessous, nous pouvons vérifier que tous les appels de fonction sont synchrones, c'est-à-dire que l'appel de fonction createAsciiChars
est non exécuté jusqu'à ce que createIntegers
termine son exécution.
scala> INT scala-execution-context-global-27 Begin.
INT scala-execution-context-global-27 End.
CHAR scala-execution-context-global-28 Begin.
CHAR scala-execution-context-global-28 End.
Faire ces appels de fonction createAsciiChars
, createIntegers
en dehors de la compréhension for
sera une exécution asynchrone.