Casos de uso para los Flujos en Scala

En la Scala no es una clase de Secuencia que es muy parecida a la de un iterador. El tema La diferencia entre el Iterador y Corriente en la Scala? ofrece algunas ideas sobre las similitudes y diferencias entre los dos.

Ver cómo el uso de una secuencia es bastante simple, pero no tengo muchas común casos de uso donde me gustaría utilizar una secuencia en lugar de otros artefactos.

Las ideas que tengo en este momento:

  • Si usted necesita para hacer uso de una serie infinita. Pero esto no parece igual que un caso de uso que a mí, para que no se ajusta a mis criterios. (Por favor corríjanme si es común y sólo tengo un punto ciego)
  • Si usted tiene una serie de datos donde cada elemento debe ser calculada, pero que puede volver a utilizar varias veces. Este es débil porque me podía cargar en una lista que es conceptualmente más fácil de seguir para un gran subconjunto de la población de programadores de software.
  • Tal vez hay un gran conjunto de datos o un computacionalmente costosos de la serie y hay una alta probabilidad de que los artículos que usted necesita no requieren visitando a todos los elementos. Pero en este caso un Iterador que sería un buen partido, a menos que usted necesita hacer varias búsquedas, en el caso de que usted podría utilizar una lista así, incluso si es un poco menos eficiente.
  • Existe una compleja serie de datos que necesita para ser reutilizados. De nuevo una lista podría ser utilizado aquí. Aunque en este caso ambos casos sería igual de difícil uso y un Arroyo sería un mejor ajuste, ya que no todos los elementos tienen que ser cargado. Pero, nuevamente, no se que común… ¿o no?

He perdido ningún grande se utiliza? O es un desarrollador de preferencia de la mayor parte?

Gracias

InformationsquelleAutor Jesse Eichar | 2010-01-19

4 Kommentare

  1. 40

    La principal diferencia entre un Stream y un Iterator es que el último es mutable y «one-shot», por así decirlo, mientras que el primero no lo es. Iterator tiene mejor memoria que la huella de Stream, pero el hecho de que es mutable puede ser un inconveniente.

    Tomar este clásico de primer generador de número, por ejemplo:

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

    Puede ser fácilmente ser escrito con un Iterator así, pero un Iterator no mantener los números primos calculada hasta el momento.

    Así, un aspecto importante de un Stream es que se puede transmitir a otras funciones sin tener que duplicar el primero, o tener que generar de nuevo y de nuevo.

    Tan caros los cálculos o listas infinitas, estas cosas se pueden hacer con Iterator así. Listas infinitas son en realidad muy útil … que simplemente no lo saben porque no tienen, por lo que han visto los algoritmos que son más complejos de lo estrictamente necesario para lidiar con las finito tamaños.

    • Otra diferencia que me gustaría añadir es que Stream nunca es perezoso en su elemento head. La cabeza de un Stream se almacena en evaluó forma. Si uno tiene una secuencia en la que ningún elemento (incluyendo la cabeza) se calcula hasta que pidió, a continuación, Iterator es la única opción.
    • Además no es la pereza en el elemento head, también se evalúa cada elemento que desea colocar. por ejemplo: "a"#::"b"#::"c"#::"d"#::Stream.empy[String].drop(3) va a evaluar «a», «b», «c» y «d» . «d», porque se convierte en la cabeza.
    • Muy interesante, conciso ejemplo para el primer generador de número. Curiosamente, si creo que en una simple Scala de consola y, a continuación, pedir a los 4000 de los números primos (no tanto en la práctica, tengo una definición alternativa que crea 100K en menos de 2 segundos) se bloquea Scala con un «GC sobrecarga superado el límite de error».
  2. 18

    Además de Daniel respuesta, tenga en cuenta que Stream es útil para el cortocircuito de las evaluaciones. Por ejemplo, supongamos que tengo un enorme conjunto de funciones que toman String y volver Option[String], y quiero mantener la ejecución de ellos, hasta que uno de ellos trabaja:

    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
    );

    Bien, yo ciertamente no desea ejecutar el toda la lista, y no hay ningún método práctico en List que dice, «el tratamiento de estos como de las funciones y ejecutar ellos hasta que uno de ellos devuelve algo distinto de None«. Qué hacer? Tal vez esto:

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

    Este toma una lista y la trata como una Stream (que en realidad no evaluar), a continuación se define un nuevo Stream que es un resultado de la aplicación de las funciones (pero que no evalúan nada aún), a continuación, busca la primera, que es definido–y aquí, por arte de magia, se mira hacia atrás y se da cuenta que se ha de aplicar el mapa, y obtener los datos correctos de la lista original–y, a continuación, se desenvuelve de Option[Option[String]] a Option[String] utilizando getOrElse.

    He aquí un ejemplo:

    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

    Pero ¿funciona? Si ponemos un println en nuestras funciones para que podamos saber si se les llama, llegamos

    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

    (Esto es con Scala 2.8; 2.7 de la aplicación a veces pasarse por uno, por desgracia. Y tenga en cuenta que usted hacer acumular una larga lista de None como sus fracasos se acumulan, pero, presumiblemente, esto es barato en comparación a su verdadero cálculo aquí.)

    • Yo en realidad un ejemplo, pero esto puede ser hecho con Iterator, así que decidí que estaba al lado del punto.
    • Concedido. Yo debería haber aclarado que esto no es Corriente específica, o tomado un ejemplo que utiliza múltiples llamadas a la misma Corriente.
  3. 7

    Yo podía imaginar, que si la encuesta de algún dispositivo en tiempo real, una Secuencia es más conveniente.

    Pensar de un GPS tracker, que devuelve la posición actual, si se solicita. Usted no puede precompute el lugar donde va a ser en 5 minutos. Usted podría utilizar durante un par de minutos solo para actualizar un camino en OpenStreetMap o usted podría utilizar para una expedición de más de seis meses en un desierto o en la selva.

    O un termómetro digital o de otro tipo de sensores que volver repetidamente nuevos datos, siempre y cuando el hardware está vivo y convertido en un archivo de registro de filtro podría ser otro ejemplo.

  4. 3

    Stream es Iterator como immutable.List es mutable.List. Favorecer la inmutabilidad impide una clase de errores, de vez en cuando en el costo de rendimiento.

    scalac en sí no es inmune a estos problemas: http://article.gmane.org/gmane.comp.lang.scala.internals/2831

    Como Daniel señala, favoreciendo la pereza más de rigor puede simplificar los algoritmos y hacer más fácil la componen.

    • Por supuesto, para aquellos que son nuevos a la pereza, la principal limitación es que se reduce la previsibilidad del código, puede conducir a heisenbugs, y puede tener graves problemas de rendimiento para algunas clases de algoritmos.

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea