• Publicidad

Dividir un array en varios arrays

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Dividir un array en varios arrays

Notapor slopal » 2006-05-13 10:54 @496

Hola, tengo un array con n elementos y necesito dividirlo en 5 arrays.

Yo lo intentaba de esta forma:

my @parte_1 = @posicions[0..$valor];

pero no me funciona :( Algo estaré haciendo mal, o ¿a lo mejor sólo esto sirve para copiarlo en una variable?

También tendré que controlar que el tamaño del inicial sea múltiplo de 5 (pero lo he estado probando en uno de 25 posiciones y nada).

Gracias por vuestra ayuda.
slopal
Perlero nuevo
Perlero nuevo
 
Mensajes: 78
Registrado: 2005-11-23 11:41 @528

Publicidad

Notapor explorer » 2006-05-13 12:56 @581

Pero... qué cosas más raras tienes que hacer...

Lo estás haciendo bien. Esa es una de las formas de sacar elementos contiguos del interior de un array.

A ver... dividir un array en 5 partes y que la primera parte sea además múltiplo de 5...

Yo he encontrado una solución, guardando las 5 partes en otro array. Nada de crear 5 variables distintas...
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl -l
  2. use Data::Dumper;
  3. use warnings;
  4. use strict;
  5.  
  6. # Generamos el vector de prueba
  7. my @posicions = ( 1 .. ( rand 80 ) );
  8. print "Longitud de posicions: ", scalar @posicions;
  9.  
  10. # Encontramos el valor de longitud de la primera división
  11. my $div1 = int( ( @posicions + 4 ) / 5 );
  12. $div1++ while $div1 % 5;
  13. print "Longitud de la primera parte: $div1";
  14.  
  15. # Largo de los subarrays restantes
  16. my $div5 = int( ( @posicions - $div1 ) / 4 + 0.9 );
  17. print "Longitud de las partes: $div5";
  18.  
  19. # SubArrays
  20. my @subarrays;                         # Aqui guardaremos las partes
  21. my $div;                               # Largo de los cortes
  22. my $antcorte;
  23. my $corte = -1;                        # Posiciones de los cortes
  24. for my $i ( 1 .. 5 ) {
  25.  
  26.     # Según sea la primera parte o no
  27.     $div = ( $i == 1 ) ? $div1 : $div5;
  28.  
  29.     # Cálculo de las posiciones
  30.     $antcorte = $corte + 1;
  31.     $corte += $div;
  32.     $corte = $#posicions if $corte >= @posicions;
  33.  
  34.     # ¡Corte!
  35.     $subarrays[ $i - 1 ] = [ @posicions[ $antcorte .. $corte ] ];
  36. }
  37.  
  38. # Presentación de todo
  39. print Dumper @subarrays;
  40.  
  41. # Acceso al 1er elemento de la 2a parte
  42. print $subarrays[1][0];
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4

Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Una prueba:
Longitud de posicions: 12
Longitud de la primera parte: 5
Longitud de las partes: 2
$VAR1 = [
          1,
          2,
          3,
          4,
          5
        ];
$VAR2 = [
          6,
          7
        ];
$VAR3 = [
          8,
          9
        ];
$VAR4 = [
          10,
          11
        ];
$VAR5 = [
          12
        ];

6
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4
Explicación:
  • Línea 7, creamos un vector de prueba
  • Líneas 11 y 12, calculamos el largo del primer array. Primero probamos a partir de un quinto del largo total, para luego ir sumando de uno en uno hasta encontrar el primer múltiplo de 5. Como int redondea a la baja, por eso sumamos 4, para colocarnos por delante del primer ítem múltiplo de 5
  • Línea 16, averiguamos el largo de los 4 arrays restantes, que será función del total de posicions menos lo que ocupe el primer array. Lo dividimos por 4 arrays que quedan por rellenar y le sumamos 0.9 para ayudar a int (por la misma razón de antes)
  • Líneas 23 a 35, tenemos un bucle que va creando los subarrays
  • Línea 26, hay que tener en cuenta la excepción del primer caso: tiene una longitud distinta a los demás
  • Líneas 29 a 31, calculamos las posiciones de corte dentro del primer array, que dependerán del valor de la anterior vuelta del bucle y del largo de la parte a recortar. Como precaución de la última parte, ponemos un control, para evitar tener un quinto array lleno de undefines
  • Línea 34. Aquí, metemos en un array (@subarrays), una referencia a otro ([]), que estará compuesto por una parte del @posicions
  • Finalmente, hacemos un volcado de la estructura generada y un ejemplo de acceso a uno de los elementos
Última edición por explorer el 2007-10-07 18:13 @800, editado 1 vez en total
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor Perl user » 2006-05-13 16:13 @717

¿Qué tal?

explorer, creo que te complicaste un poco la solución. En general pues, ese problema es sencillo de resolverse utilizando splice, puesto que shift, pop, unshift, etc., están codificados utilizando splice() debajo de ellos.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/us/bin/perl
  2. use strict;
  3. use warnings;
  4. use Data::Dumper;
  5.  
  6. my @a   = 1 .. 24;
  7. my $tot = 5;
  8. my $len = scalar @a / $tot;
  9. my @AoA;
  10. push @AoA, [ splice @a, 0, $len ] while $tot-- > 0;
  11. push @{ $AoA[0] }, shift @a || shift @{ $AoA[ 1 + rand $#AoA ] } while @{ $AoA[0] } % 5;
  12.  
  13. print Dumper( \@AoA );
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Como está un poco críptico vamos a explicarlo:

Primero, para fines de ejemplo, generamos un array de N cantidad de elementos, en este caso le puse 24. Después establecemos el tamaño de las particiones a crear y luego el tamaño de elementos exactos por cada una de ellas. Una vez hecho esto, declaramos el array que almacenará cada partición como una referencia a otro array, es decir un Array of Arrays.

El primer pop lo que hace es, ir tomando $len cantidad de elementos de nuestro array original e irlos poniendo en cada una de las particiones, como ya se mencionó pone cada partición como una referencia a un array ( utilizando el constructor [] ).

Luego viene la parte interesante, la parte donde el autor original del mensaje requiere que al menos la primera partición sea múltiplo de 5. Esto se logra de la siguiente manera: Como se pudo observar, nunca vaciamos completamente nuestro array original, solo generamos las particiones pedidas (5 para este caso) con una cantidad igual de elementos, los demás elementos quedaron en el array original. Para esto lo que vamos a hacer es comprobar que mientras la primer partición no sea múltiplo de 5, vamos a tomar elementos de nuestro array original mientras este contenga, en caso contrario, tomaremos algún elemento de manera aleatoria de otra de las particiones ya creadas.

Y con esto, solucionamos el problema propuesto.

Saludos,
Marco A. Manzo
[email protected]
http://www.unixmonkeys.com/amnesiac/
Perl Programming Language
Perl user
Maestro honorario
Maestro honorario
 
Mensajes: 271
Registrado: 2004-11-03 21:11 @924

Notapor explorer » 2006-05-14 07:14 @343

Bien... pero...
  • Estamos en el foro Básico :)
  • splice modifica el vector original
  • La división '/' es sensible a la longitud del vector (por el tema de los redondeos de enteros), por lo que hay que tomar precauciones.
Por ejemplo, si ejecuto tu solución tal cual la pones, me sale:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
$VAR1 = [
          [
            1,
            2,
            3,
            4,
            21
          ],
          [
            5,
            6,
            7,
            8
          ],
          [
            9,
            10,
            11,
            12
          ],
          [
            13,
            14,
            15,
            16
          ],
          [
            17,
            18,
            19,
            20
          ]
        ];
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4
es decir, que los elementos 22, 23 y 24 siguen estando dentro de @a: print Dumper \@a
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
$VAR1 = [
          22,
          23,
          24
        ];
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4
Debido a que int(24/5) da 4, por lo que son 5*4=20 elementos los que se meten en los arrays, más el 21º que se mete en el primero para llegar a ser múltiplo de 5. El mismo problema se presenta con valores como 14, 18, 19, 22, 23, 26 al 29, 39, 43, 44, 47 al 49, 51 al 54, 64, 68, 69, 72 al 74, etc., etc. Y a mi me pareció entender en la formulación del problema que había que repartir todo el array en 5.

Otro detalle muy curioso es lo de la inclusión de forma aleatoria de elementos de otros arrays (una idea muy buena que me lo apunto para el futuro), y se podría decir que en muchos casos no serviría (por el tema de aparecer los elementos de forma desordenada) pero como en la formulación de la pregunta de slopal no ha puesto ningún requerimiento de orden, la solución de Perl User de meter elementos al azar es válida (salvo en el caso de dejar algún array sin elementos, por lo que entonces estaríamos en otro problema).

Yo creo que la responsable es slopal por no dar más detalles. :wink:
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor Perl user » 2006-05-14 11:49 @534

¿Qué tal?

En efecto estamos en el foro básico. Sobre el problema de los valores enteros tienes razón, para eso recomiendo POSIX::ceil. Y la propuesta de que los números no están ordenados también es cierta, pero en la especificación dada por slopal no veo que indique que deban estar ordenados, si así lo fuese entonces habría que modificar un poco el script.

Sobre splice(), así es, modifica el array, pero si ya lo va a tener dividido y usable no le veo gran problema, a menos que vaya a reutilizar el array inicial.

Saludos,
Marco A. Manzo
[email protected]
http://www.unixmonkeys.com/amnesiac/
Perl Programming Language
Perl user
Maestro honorario
Maestro honorario
 
Mensajes: 271
Registrado: 2004-11-03 21:11 @924

Notapor slopal » 2006-05-15 10:42 @487

Venga.. tiradme piedras :P

¡Lo necesitaba ordenado! :P

(Y no me hace falta reutilizar el array original). De todas formas, ¡me miraré esto y algo sacaré! No le deis más vueltas, si no lo consigo ya os digo algo.

Merci
slopal
Perlero nuevo
Perlero nuevo
 
Mensajes: 78
Registrado: 2005-11-23 11:41 @528

Notapor Perl user » 2006-05-15 12:40 @569

Bueno, en vez tirarte piedras, te propongo otra solución, con los puntos aclarados en tu comentario anterior, y los puntos expuestos por explorer.

Esta vez es una solución igual de minimalista, pero que involucra puro juego de índices y array slices.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5. use POSIX 'floor';
  6. use Data::Dumper;
  7.  
  8. sub PARTITIONS () { 5 }
  9.  
  10. my @a = 1..9;
  11. my $part_size = floor( @a / PARTITIONS );
  12.  
  13. $part_size++ while $part_size % 5;
  14. my @AoA = ( [ @a[ 0..$part_size - 1 ] ] );
  15.  
  16. my $part_left = PARTITIONS - 1;
  17. my $start = $part_size;
  18. $part_size = floor( ( $#a - $part_size ) / $part_left );
  19. my $offset = $start + $part_size;
  20. while( $part_left-- > 0 ) {
  21.     push @AoA, [ @a[ $start .. $offset ] ];
  22.     $start += $part_size + 1;
  23.     $offset = $start + $part_size > $#a ?  $#a : $start + $part_size;
  24. }
  25.  
  26. print Dumper( \@AoA );
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Saludos,
Marco A. Manzo
[email protected]
http://www.unixmonkeys.com/amnesiac/
Perl Programming Language
Perl user
Maestro honorario
Maestro honorario
 
Mensajes: 271
Registrado: 2004-11-03 21:11 @924

Notapor explorer » 2006-05-17 15:42 @696

Como ayer estuve de viaje, tuve mucho tiempo para pensar en otra solución, pero no me salía nada diferente.

Lo más, intentar hacer una versión más compacta.

Lo malo es que entonces entramos en el terreno de Perl ofuscado :)
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use integer;
  2. @a=1..$ARGV[0];
  3. @i=($x,map{$y=(@a-($x=(@a+4)/5))/4}1..4);
  4. $i[0]+=@a-$x-4*$y;
  5. $i[4-($i[0]++,$z++%4)]--while$i[0]%5;
  6. push(@AoA,[@a[$k..($k+$i[$_])-1]]),$k+=$i[$_] for 0..4;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
La longitud del vector de prueba la pasamos como primer argumento.

Es curioso... esta solución funciona parecido a la primera que dí, pero prima mucho más el tamaño del primer subarray.

En cuanto al funcionamiento, es lo misma que la primera, salvo que se mete en el primer subarray los elementos sobrantes después de hacer los cálculos, que es una idea sacada de la solución de Perl User.

La novedad es que ahora se basa en hacer cálculos independientes en los tamaños de los subarrays en vez de calcular un tamaño fijo para todos.

Lo dejo aquí por si alguien se quiere entretener en adivinar cómo funciona.
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor slopal » 2006-05-23 12:20 @556

explorer... ¡disfruta del viaje hombre! :lol:

Gracias a todos, con un poco de aquí y otro de allí (y algunos cambios que tuve que hacer) ya lo he conseguido :)
slopal
Perlero nuevo
Perlero nuevo
 
Mensajes: 78
Registrado: 2005-11-23 11:41 @528


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 0 invitados