Cómo dividir una Lista de elementos en las listas en la mayoría de los elementos de N?

ex: Dada una lista con 7 elementos, crear grupos de 4, dejando a este último grupo, posiblemente con menos elementos.

split(List(1,2,3,4,5,6,"seven"),4)

=> List(List(1,2,3,4), List(5,6,"seven"))

5 Comentarios

  1. 193

    Creo que usted está buscando grouped. Devuelve un iterador, pero se puede convertir el resultado a una lista,

    scala> List(1,2,3,4,5,6,"seven").grouped(4).toList
    res0: List[List[Any]] = List(List(1, 2, 3, 4), List(5, 6, seven))
    • Scala listas tienen algo para todo.
    • Tengo una pregunta extraña. Por el mismo caso si puedo convertir los datos en una secuencia, tengo un Objeto Stream. ¿Por qué es eso?
    • Eso suena como una cuestión separada. Scala tiene una misteriosa gnome que elige una estructura de datos, y se eligió una Secuencia para usted. Si desea una Lista, usted debe solicitar una Lista, pero también puede simplemente confiar en el de gnome juicio.
  2. 9

    Hay manera mucho más fácil hacer la tarea utilizando el método de deslizamiento.
    Funciona de esta manera:

    val numbers = List(1, 2, 3, 4, 5, 6 ,7)

    Digamos que usted desea dividir la lista en pequeñas listas de tamaño 3.

    numbers.sliding(3, 3).toList

    le dará

    List(List(1, 2, 3), List(4, 5, 6), List(7))
  3. 8

    O si usted desea hacer su propio:

    def split[A](xs: List[A], n: Int): List[List[A]] = {
      if (xs.size <= n) xs :: Nil
      else (xs take n) :: split(xs drop n, n)
    }

    Uso:

    scala> split(List(1,2,3,4,5,6,"seven"), 4)
    res15: List[List[Any]] = List(List(1, 2, 3, 4), List(5, 6, seven))

    editar: al revisar esta 2 años más tarde, no recomiendo esta aplicación desde la size es O(n), y por lo tanto, este método es O(n^2), lo cual explicaría por qué la incorporada en el método se vuelve más rápido para listas grandes, como se señaló en los comentarios de abajo. Usted podría implementar de manera eficiente como sigue:

    def split[A](xs: List[A], n: Int): List[List[A]] =
      if (xs.isEmpty) Nil 
      else (xs take n) :: split(xs drop n, n)

    o incluso (ligeramente) más eficientemente usando splitAt:

    def split[A](xs: List[A], n: Int): List[List[A]] =
      if (xs.isEmpty) Nil 
      else {
        val (ys, zs) = xs.splitAt(n)   
        ys :: split(zs, n)
      }
    • xs splitAt n es una alternativa a la combinación xs take n y xs drop n
    • esto va a explotar la pila, considere la posibilidad de una implementación recursiva
    • cierto, pero es necesario extraer los resultados temporales de vals por lo que agrega un par de líneas a un método. Hice una rápida referencia y parece que el uso de splitAt en lugar de take/dropmejora el rendimiento promedio de alrededor de 4%; ambos son de 700 a 1000% más rápido que .grouped(n).toList!
    • Wow. Cualquier pensamiento acerca de por qué grouped-toList es tan lento? Que suena como un error.
    • Tienes razón, en casos extremos, pero su implementación depende de lo que usted lo está utilizando para. Para el OP de caso de uso (si grouped no existen :)), la simplicidad es el factor primordial. Para la biblioteca estándar, la estabilidad y el rendimiento debe trump elegancia. Pero hay un montón de ejemplos en Programación en Scala y las bibliotecas estándar de normal-recursiva (en lugar de cola-recursivo) llamadas; es un estándar y arma importante en la FP caja de herramientas.
    • En realidad no, me miró brevemente a la fuente de la que muestra que grouped crea algo llamado GroupedIterator que se repite dentro de una expresión; tal vez la máquina virtual es menos capaz de optimizar esto que una función recursiva simple, pero realmente no tengo idea. Me imagino que no hay mucho margen para la medición del rendimiento y la optimización dentro de las colecciones de la biblioteca!
    • un poco más de investigación muestra que actaully la incorporada en el grouped es mucho más rápido para grandes listas (que se divide en cientos de grupos). El crossover parece venir cuando las listas de llegar a alrededor de 200 en tamaño. No hay error!
    • Una interesante discusión entre los chicos. Gracias por la respuesta. Ahora mismo estoy usando agrupados y está muy bien.
    • Usted realmente no necesita la verificación if (zs.isEmpty). Ya está cubierta por el primer caso. Por lo que sólo podría escribir ys :: split(zs, n)
    • buen punto, gracias, actualizado
    • Compare ahora su primera solución y la actualización de uno 🙂 Te quita demasiado: splitAt era bueno. Ahora usted llame take y drop de nuevo. Lo siento para el picking.
    • Ahora he mostrado el splitAt versión demasiado, aunque no estoy convencido de que este es mejor. Como se mencionó anteriormente, la diferencia entre el uso que los y take + drop es mucho más pequeño de lo que parece: splitAt, take, y drop todo ser O(n), entonces sólo estamos hablando de factores constantes… splitAt y take son básicamente las mismas, y la contstant factor de drop es pequeño en comparación (sólo eliminar una variable).
    • Estoy de acuerdo con usted acerca de factor constante. Es por eso que yo no entiendo código actualizado, ¿por qué crees que es mucho más eficiente. En mi opinión, no hay mucha diferencia de su primer código.
    • Para un determinado n, take, splitAt y drop tomar una constante de tiempo, no importa lo grande xs es. Mientras que size toma tiempo proporcional a la longitud de xs. Ya que estos se realizan para cada elemento, el tiempo de ejecución aumenta cuadráticamente si utiliza size. Podemos eliminar este problema mediante el uso de isEmpty, que es constante en el tiempo.
    • No es un caso extremo, se va a pasar por seguro que en algún momento en el tiempo. Tienes que hacerlo en una cola recursiva de moda el uso de un acumulador tal vez

  4. 3

    Estoy adición de una cola versión recursiva del método split ya que hay algo de debate de la cola de la recursividad frente a la recursividad. He utilizado el tailrec anotación para forzar al compilador a quejarse en caso de que la aplicación no es, de hecho, la cola de recusive. La cola de la recursividad creo que se convierte en un bucle bajo el capó y por lo tanto no causa problemas incluso para una lista grande como la pila no va a crecer indefinidamente.

    import scala.annotation.tailrec
    
    
    object ListSplitter {
    
      def split[A](xs: List[A], n: Int): List[List[A]] = {
        @tailrec
        def splitInner[A](res: List[List[A]], lst: List[A], n: Int) : List[List[A]] = {
          if(lst.isEmpty) res
          else {
            val headList: List[A] = lst.take(n)
            val tailList : List[A]= lst.drop(n)
            splitInner(headList :: res, tailList, n)
          }
        }
    
        splitInner(Nil, xs, n).reverse
      }
    
    }
    
    object ListSplitterTest extends App {
      val res = ListSplitter.split(List(1,2,3,4,5,6,7), 2)
      println(res)
    }
    • Esta respuesta podría ser mejorada mediante la adición de un poco de explicación. Dado que el aceptado la respuesta parece ser que la canónica, la intención manera de hacer esto, usted debe explicar por qué alguien preferiría esta respuesta.
  5. 0

    Creo que esta es la aplicación utilizando splitAt en lugar de tomar/dejar

    def split [X] (n:Int, xs:List[X]) : List[List[X]] =
        if (xs.size <= n) xs :: Nil
        else   (xs.splitAt(n)._1) :: split(n,xs.splitAt(n)._2)

Dejar respuesta

Please enter your comment!
Please enter your name here