50 votes

Cas d'utilisation des flux dans Scala

En Scala, il y a une classe de Flux est très semblable à un itérateur. Le sujet de la Différence entre Itérateur et Flux en Scala? propose quelques pistes de réflexion sur les similitudes et les différences entre les deux.

Voir comment utiliser un flux de données est assez simple, mais je n'ai pas de très nombreuses communes des cas d'utilisation où je voudrais utiliser un flux plutôt que d'autres artefacts.

Les idées que j'ai droit:

  • Si vous avez besoin de faire usage d'une série infinie. Mais cela ne semble pas comme sur un cas d'utilisation pour moi, donc ça ne correspond pas à mes critères. (Merci de me corriger si il est courant et je n'ai qu'une tache aveugle)
  • Si vous avez une série de données, où chaque élément doit être calculée, mais que vous souhaitez réutiliser plusieurs fois. C'est faible, parce que je pouvais juste de le charger dans une liste qui est conceptuellement plus facile à suivre pour un grand sous-ensemble de la population des développeurs.
  • Peut-être il ya un grand ensemble de données ou d'un calcul coûteux de la série et il y a une forte probabilité que les éléments dont vous avez besoin ne nécessitera pas de visiter tous les éléments. Mais dans ce cas, un Itérateur serait un bon match sauf si vous avez besoin de faire plusieurs recherches, dans ce cas, vous pourriez utiliser une liste même si elle serait un peu moins efficace.
  • Il y a une série complexe de données qui doivent être réutilisés. De nouveau, une liste pourrait être utilisé ici. Bien que dans ce cas les deux cas, serait également difficile à utiliser et d'un Flux serait plus adapté car pas tous les éléments doivent être chargés. Mais encore une fois pas commun... ou est-il?

Donc, ai-je raté une grosse utilise? Ou est-ce un développeur de préférence, pour la plupart?

Merci

40voto

Daniel C. Sobral Points 159554

La principale différence entre un Stream et Iterator , c'est que ce dernier est mutable et "one-shot", pour ainsi dire, alors que le premier ne l'est pas. Iterator a une meilleure mémoire que Stream, mais le fait qu'il est mutable peut être gênant.

Prendre ce classique du premier générateur de nombres, par exemple:

def primeStream(s: Stream[Int]): Stream[Int] =
  Stream.cons(s.head, primeStream(s.tail filter { _ % s.head != 0 }))
val primes = primeStream(Stream.from(2))

Il peut facilement être écrit avec un Iterator , mais un Iterator ne garder que les premiers calculées jusqu'à présent.

Donc, un aspect important d'un Stream , c'est que vous pouvez passer à d'autres fonctions sans avoir dupliqué le premier, ou d'avoir à générer de nouveau et de nouveau.

Comme pour le coûteux calculs/infini listes, ces choses peuvent être faites avec Iterator ainsi. Infini listes sont en fait très utiles -- vous ne savez pas parce que vous ne l'avez pas, si vous avez vu les algorithmes qui sont plus complexes que ce qui est strictement nécessaire pour traiter forcée finis tailles.

18voto

Rex Kerr Points 94401

En plus de Daniel réponse, gardez à l'esprit que le Stream est utile pour de court-circuit des évaluations. Par exemple, supposons que j'ai un énorme ensemble de fonctions qui prennent de la Chaîne et de l'Option de retour[Chaîne], et je veux garder leur exécution jusqu'à ce que l'un d'entre eux fonctionne:

val stringOps = List(
  (s:String) => if (s.length>10) Some(s.length.toString) else None ,
  (s:String) => if (s.length==0) Some("empty") else None ,
  (s:String) => if (s.indexOf(" ")>=0) Some(s.trim) else None
);

Eh bien, je ne veux certainement pas à exécuter l' ensemble de la liste, et il n'est pas une méthode pratique sur la Liste qui dit, "traitez-les comme des fonctions et de les exécuter jusqu'à ce que l'un d'entre eux renvoie à quelque chose d'autre que None". Que faire? Peut-être ceci:

def transform(input: String, ops: List[String=>Option[String]]) = {
  ops.toStream.map( _(input) ).find(_ isDefined).getOrElse(None)
}

Cela prend une liste et le traite comme un Flux (qui n'a pas réellement d'évaluer quoi que ce soit), puis définit un nouveau Flux de données qui est un résultat de l'application de fonctions (mais cela ne veut pas évaluer rien encore), puis recherche le premier qui est définie--et là, comme par magie, il regarde en arrière et se rend compte qu'il doit appliquer la carte et obtenir les bonnes données à partir de la liste d'origine--et puis déballe de l'Option[Option[String]] pour l'Option[String] à l'aide de getOrElse.

Voici un exemple:

scala> transform("This is a really long string",stringOps)
res0: Option[String] = Some(28)

scala> transform("",stringOps)
res1: Option[String] = Some(empty)

scala> transform("  hi ",stringOps)
res2: Option[String] = Some(hi)

scala> transform("no-match",stringOps)
res3: Option[String] = None

Mais ça fonctionne? Si l'on met une println dans nos fonctions, nous pouvons donc dire que si on les appelle, nous obtenons

val stringOps = List(
  (s:String) => {println("1"); if (s.length>10) Some(s.length.toString) else None },
  (s:String) => {println("2"); if (s.length==0) Some("empty") else None },
  (s:String) => {println("3"); if (s.indexOf(" ")>=0) Some(s.trim) else None }
);
// (transform is the same)

scala> transform("This is a really long string",stringOps)
1
res0: Option[String] = Some(28)

scala> transform("no-match",stringOps)                    
1
2
3
res1: Option[String] = None

(C'est avec Scala 2.8; 2.7 mise en œuvre du parfois de dépassement par un, malheureusement. Et notez que vous ne accumuler une longue liste d'Aucun comme des échecs de revenir, mais sans doute cela est peu coûteux par rapport à votre vrai calcul ici.)

7voto

user unknown Points 15555

Je pouvais imaginer, que si vous votez quelques périphérique en temps réel, un Flux est plus pratique.

Pensez à un traqueur de GPS, qui renvoie à la position réelle si vous le demandez. Vous ne pouvez pas précalculer l'endroit où vous serez en 5 minutes. Vous pouvez l'utiliser pour quelques minutes seulement de réaliser un chemin d'accès dans OpenStreetMap ou vous pouvez l'utiliser pour une expédition de plus de six mois dans un désert ou la forêt tropicale.

Ou d'un thermomètre numérique ou d'autres types de capteurs qui, à plusieurs reprises le retour de nouvelles données, dès lors que le matériel est vivant et tourné sur un fichier journal filtre pourrait être un autre exemple.

3voto

retronym Points 35066

Stream est Iterator comme immutable.List est mutable.List. En favorisant l'immuabilité empêche une classe de bugs, parfois au détriment des performances.

scalac elle-même n'est pas à l'abri de ces problèmes: http://article.gmane.org/gmane.comp.lang.scala.internals/2831

Comme Daniel souligne, en favorisant la paresse plus de rigueur peut simplifier les algorithmes et le rendre plus facile pour les composer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X