• Publicidad

Combinar elementos de array

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

Combinar elementos de array

Notapor Peich69 » 2014-01-22 15:07 @671

Hola.

Lo que quiero es saber el número de líneas que contienen la palabra X de un archivo. Esta es la primera aproximación que he hecho
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl -w
  2.  
  3. my @lista_filtros = (X, Y);
  4. my $filtro;
  5. my $archivo;
  6. my @match;
  7. my $num_match;
  8.  
  9. $archivo = $ARGV[0];
  10.  
  11. foreach $filtro(@lista_filtros)
  12. {
  13.     @match = `grep $filtro $archivo`;
  14.     $num_match = scalar(@match);
  15.     print 'Lineas con '.$filtro.': '.$num_match, "\n";
  16.    
  17. }
  18. print "Adios\n";
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4


Ahora me gustaría poder combinar los filtros, es decir, saber el número de líneas que contienen la palabra X, la palabra Y y las dos palabras.

He leído que con el grep -e puedo hacerlo, pero el problema es que no sé cómo montar la variable $filtro.

Mi idea es asignar a $filtro cada elemento del array por separado (primero X, luego Y) y todas las combinaciones posibles (en este caso solo X y Y) en un bucle.

Tengo varios problemas. El principal es que supongo que se puede hacer pero no sé cómo.

Pero además tendría que incluir el -e al principio de cada elemento para que cuando me combine los elemento la $filtro sea "-e X -e Y".

¿Qué se os ocurre?
Gracias
Peich69
Perlero nuevo
Perlero nuevo
 
Mensajes: 18
Registrado: 2013-03-19 08:49 @409

Publicidad

Re: Combinar elementos de array

Notapor explorer » 2014-01-22 17:51 @785

No es necesario usar el comando grep del sistema. Perl ya cuenta con su propia función grep(). Y es mejor, porque puedes encontrarte con un sistema operativo que no tenga el comando grep (Windows, claro).

@match = grep { /$filtro/ } @lineas_archivo;

Pero no es la única función para buscar. También se pueden usar las funciones index() y rindex().

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use v5.16;
  3. use autodie;                    # «Es mejor morir que regresar con deshonor» --proverbio Klingon
  4. use utf8::all;                  # Turn on UTF-8. All of it.
  5.  
  6. my @filtros = qw(viride AAZ);
  7. my @contadores;
  8.  
  9. for my $archivo (@ARGV) {
  10.     next if not -f $archivo;            # si no podemos leer el archivo, pasamos al siguiente
  11.  
  12.     open my $ARCHIVO, $archivo;
  13.    
  14.     while (my $linea = <$ARCHIVO>) {
  15.  
  16.         my $encontrados_en_linea = 0;
  17.  
  18.         while(my($i, $filtro) = each @filtros) {
  19.  
  20.             my $encontrado = index($linea, $filtro) != -1;      # buscamos $filtro dentro de $linea.
  21.                                                                 # index() devuelve la posición, o -1 (no encontrado).
  22.                                                                 # 'index() != -1' devuelve 1 si
  23.                                                                 # $filtro se encuentra. 0, si no.
  24.  
  25.             $contadores[$i]       += $encontrado;               # contador individual de filtros
  26.  
  27.             $encontrados_en_linea += $encontrado;               # filtros encontrados en la línea
  28.         }
  29.  
  30.         if ($encontrados_en_linea == @filtros) {                # para el caso de que aparezcan todos en la misma línea
  31.             $contadores[@filtros]++;                            # ese contador lo ponemos al final de @contadores
  32.         }
  33.     }
  34.    
  35.     close $ARCHIVO;
  36.  
  37.     say "Archivo $archivo:";
  38.  
  39.     while(my($i, $filtro) = each @filtros) {
  40.         say "Líneas con filtro [$filtro]: $contadores[$i]";
  41.     }
  42.     say "Líneas con todos los filtros: $contadores[@filtros]";
  43. }
  44.  
  45. __END__
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4
Con este archivo de entrada:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
340516023       T. reesei       8.00E-003       Nep1    AAC97382        Fusarium oxysporum      2.096910013
358382142       T. virens       4.00E-012       Nep2    AAC97383        Fusarium oxysporum      11.3979400087
358379635       T. virens       5.00E-003       Nep2    ABO32369        Moniliophthora perniciosa       2.3010299957
358398385       T. atroviride   5.00E-003       Nep3    ABO32370        Moniliophthora perniciosa       2.3010299957
358401522       T.atroviride    2.00E-003       Ecp2    CAA78401        Cladosporium flavum     2.6989700043
358398590       T. atroviride   2.00E-023       GIP1    AAL11720        Phytophtora sojae       22.6989700043
340522571       T. atroviride   6.00E-023       GIP2    AAL11721        Phytophtora sojae       22.2218487496
358387035       T. virens       2.00E-023       GIP3    AAL11722        Phytophtora sojae       22.6989700043
358382531       T. virens       1.00E-020       GIP4    AAL11723        Phytophtora sojae       20
358378107       T. virens       4.00E-003       GIP5    AAL11724        Phytophtora sojae       2.3979400087
358396651       T. atroviride   5.00E-053       Sm1     AAZ80388        Trichoderma virens      52.3010299957
358396592       T. atroviride   6.00E-018       Sm2     AAZ80389        Trichoderma virens      17.2218487496
358390203       T. atroviride   8.00E-081       Sm3     AAZ80390        Trichoderma virens      80.096910013
358389999       T. atroviride   3.00E-028       Sm4     AAZ80391        Trichoderma virens      27.5228787453
340514840       T. reesei       8.00E-028       Sm5     AAZ80392        Trichoderma virens      27.096910013
340514523       T. reesei       3.00E-054       Sm6     AAZ80393        Trichoderma virens      53.5228787453
340513772       T. reesei       7.00E-088       Sm7     AAZ80394        Trichoderma virens      87.15490196
358388225       T. virens       4.00E-029       Sm8     AAZ80395        Trichoderma virens      28.3979400087
358383008       T. virens       1.00E-051       Sm9     AAZ80396        Trichoderma virens      51
358385840       T. virens       1.00E-018       Sm10    AAZ80397        Trichoderma virens      18
358388007       T. virens       6.00E-102       Sm11    AAZ80398        Trichoderma virens      101.2218487496
340522155       T. reesei       3.00E-017       Nuk7    ABM05490.1      Phytophtora infestans   16.5228787453
358386691       T. virens       2.00E-017       nuk7    ABM05490.2      Phytophtora infestans   16.6989700043
358394634       T. atroviride   1.00E-016       AVR-Pita        ABM30144        Moniliophthora perniciosa       16
340518992       T. reesei       1.00E-016       AVR-Pita        ABM30145        Moniliophthora perniciosa       16
358386019       T. virens       9.00E-016       AVR-Pita        ABM30146        Moniliophthora perniciosa       15.0457574906
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
sale:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Archivo lista2.txt:
Líneas con filtro [viride]: 9
Líneas con filtro [AAZ]: 11
Líneas con todos los filtros: 4
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Esta no es la mejor solución. Hay soluciones más óptimas, pero requieren usar otros trucos.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Combinar elementos de array

Notapor Peich69 » 2014-01-23 01:19 @096

Gracias, explorer.

Pero lo que estoy buscando exactamente no son las líneas que contienen todos los filtros. Busco una manera de evitar el uso de un algoritmo (solo la palabra hace que me tiemblen las piernas) para que si doy un array de filtros (azul, rojo, verde) me busque en las líneas:
Azul
Rojo
Verde
Azul y Rojo
Azul y Verde
Rojo y Verde
Azul, Rojo y Verde

(El orden no tiene importancia)
Al principio había pensado hacerlo manual, algo así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl -w
  2. use strict;
  3. my @filtros;
  4. my $filtro;
  5.  
  6. @filtros = ("Verde", "Rojo", "Azul");
  7. $filtro = $filtros[0];
  8. &busqueda_en_lineas($filtro);
  9. $filtro = $filtros[1];
  10. &busqueda_en_lineas($filtro);
  11. $filtro = $filtros[2];    
  12. &busqueda_en_lineas($filtro);
  13. $filtro = '-e '.$filtros[0].' -e '.$filtros[1];  # lo del comando grep, de momento, no lo puedo cambiar. Esa línea tiene más cosas a parte del grep.
  14. &busqueda_en_lineas($filtro);
  15. $filtro = '-e '.$filtros[0].' -e '.$filtros[2];
  16. &busqueda_en_lineas($filtro);
  17. $filtro = '-e '.$filtros[1].' -e '.$filtros[2];
  18. &busqueda_en_lineas($filtro);
  19. $filtro = '-e '.$filtros[0].' -e '.$filtros[1].' -e '.$filtros[2];
  20. &busqueda_en_lineas($filtro);
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Pero, claro, esto me sirve para lo que quiero hacer ahora, pero me obliga a modificar el script cada vez que quiero añadir un nuevo filtro. Me gustaría que si decido poner 4 filtros me genere automáticamente todas las combinaciones posibles con 1, 2, 3 y 4 elementos. Aparte la estructura es terrible. O sea, esto es un bucle, debería poder simplificarlo.

Gracias
Peich69
Perlero nuevo
Perlero nuevo
 
Mensajes: 18
Registrado: 2013-03-19 08:49 @409

Re: Combinar elementos de array

Notapor explorer » 2014-01-23 11:21 @515

Pues el caso es que sí que hay que usar un algoritmo, y además, tú ya lo estás indicando en el asunto de este hilo: combinaciones de los elementos de un array.

En CPAN hay unos cuántos módulos dedicados a la generación de combinaciones de elementos. Uno de ellos es Algorithm::Combinatorics. Con su ayuda podemos generar todas las combinaciones de los filtros.

Luego, para que el código no tengas que editarlo cada vez que cambien los filtros, los puedes pasar por la línea de argumentos.

Este es un ejemplo, que resuelve el problema, con la información que nos has dado hasta ahora sobre los filtros.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. #
  3. # Cuenta las apariciones de varios palabras, y combinaciones de ellas,
  4. # a lo largo de un archivo. Informa del número de líneas en las que aparecen
  5. # las distintas combinaciones.
  6. #
  7. # Joaquín Ferrero, 20140123.
  8. #
  9. # Uso: filtrado.pl <archivo> <filtro1> [ <filtro2> [ ... ]]
  10.  
  11. use v5.16;                                                              # Activar todas las nuevas características
  12. use autodie;                                                            # «Es mejor morir que regresar con deshonor» --Proverbio Kinglon
  13. use utf8::all;                                                          # Activar todo el soporte para UTF8
  14.  
  15. use Memoize;                                                            # truco de usar memoria en lugar de CPU
  16. use Algorithm::Combinatorics 'subsets';                                 # generador de subconjuntos de combinaciones
  17.  
  18. @ARGV > 1                                                               # comprobamos que se pasan los argumentos correctos
  19.     or die "Uso: $0 <archivo> <filtro1> [ <filtro2> [...]]\n";
  20.  
  21. my($archivo, @filtros) = @ARGV;                                         # leemos los argumentos
  22.  
  23. -f $archivo                                                             # comprobamos que el archivo lo podemos leer
  24.     or die "ERROR: No encuentro el archivo [$archivo]\n";
  25.  
  26. memoize('combinación');                                                        # indicamos que esa subrutina será la "recordada"
  27.  
  28. my %contador;                                                           # contadores para cada combinación
  29.  
  30. open my $ARCHIVO, $archivo;                                             # abrimos el archivo. autodie se encarga de lo demás
  31.  
  32. while (my $línea = <$ARCHIVO>) {                                       # por cada $línea del $ARCHIVO...
  33.  
  34.     my @filtros_encontrados;                                            # filtros que aparecen en esa línea
  35.  
  36.     for my $filtro (@filtros) {                                         # para todos los @filtros...
  37.         my $encontrado  =  -1 != index $línea, $filtro;                        # ¿hemos $encontrado el $filtro en la $línea?
  38.         push @filtros_encontrados, $filtro if $encontrado;              # sí, lo guardamos
  39.     }
  40.  
  41.     for my $combinación (combinación(@filtros_encontrados)) {         # generamos todas las combinaciones de los filtros encontrados
  42.         $contador{$combinación}++;                                     # sumamos una aparición más por cada $combinación
  43.     }
  44. }
  45.  
  46. close $ARCHIVO;                                                         # cerramos archivo
  47.  
  48. for my $combinación (sort keys %contador) {                            # para todos los contadores
  49.     say "Líneas con [$combinación]: $contador{$combinación}";                # indicamos las veces que aparece cada $combinación
  50. }
  51.  
  52. sub combinación {                                                      # generador de combinaciones
  53.     my @filtros = @_;                                                   # filtros a combinar
  54.     my @combinaciones;                                                  # almacén del resultado
  55.  
  56.     for my $comb_ref (subsets(\@filtros)) {                             # para todas las combinaciones de esos @filtros...
  57.         my $combinación = join '|', sort @$comb_ref;                   # pasamos esa combinación a escalar
  58.         push @combinaciones, $combinación if $combinación;            # que almacenamos en el resultado
  59.     }
  60.  
  61.     return @combinaciones;                                              # y devolvemos
  62. }
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
Con el archivo de prueba anterior, lista2.txt, y llamando al programa con

perl filtrado.pl lista2.txt viride AAZ virens

sale:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Líneas con [AAZ]: 11
Líneas con [AAZ|virens]: 11
Líneas con [AAZ|virens|viride]: 4
Líneas con [AAZ|viride]: 4
Líneas con [virens]: 18
Líneas con [virens|viride]: 4
Líneas con [viride]: 9
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

El funcionamiento es el siguiente: por cada línea del archivo, miro a ver qué filtros soy capaz de encontrar en ella. Cuando sé cuáles son, uso subsets del módulo Algorithm::Combinatorics para que me genere todas las combinaciones de esos filtros. Un ejemplo sencillo del módulo:
Sintáxis: [ Descargar ] [ Ocultar ]
  1. > perl -MAlgorithm::Combinatorics=subsets -E '@x=qw(a b c); for (subsets(\@x)) { say "@$_" }' 
  2. a b c 
  3. b c 
  4. a c 
  5. a b 
  6.  
En general, el número de subconjuntos que se generan es 2^n, siendo n el número de elementos. Para 3 elementos, son 8 subconjuntos (en el ejemplo ves siete, pero hay un octavo más, que es la línea en blanco, al final).

Como esta operación de generar las combinaciones es muy pesada y siempre la misma (el mismo grupo de filtros), usamos el módulo Memoize para que cada llamada a la subrutina combinación() se realice una sola vez, por cada llamada distinta de @filtros. Esto hace aumentar la velocidad muchísimo, a costa de ocupar más memoria.

Luego, solo tenemos que ir sumando 1 a cada combinación de los filtros encontrados en esa línea.

Al final del bucle, sacamos los contadores.

Otro ejemplo. Para este texto de entrada:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Azul
Rojo
Verde
Azul y Rojo
Azul y Verde
Rojo y Verde
Azul, Rojo y Verde
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4
y esta llamada: perl filtrado.pl kk.txt Azul Rojo Verde, sale:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Líneas con [Azul]: 4
Líneas con [Azul|Rojo]: 2
Líneas con [Azul|Rojo|Verde]: 1
Líneas con [Azul|Verde]: 2
Líneas con [Rojo]: 4
Líneas con [Rojo|Verde]: 2
Líneas con [Verde]: 4
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Seguro que hay más formas de hacerlo.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Combinar elementos de array

Notapor Peich69 » 2014-01-31 06:45 @323

Muchas gracias, explorer.
Peich69
Perlero nuevo
Perlero nuevo
 
Mensajes: 18
Registrado: 2013-03-19 08:49 @409


Volver a Básico

¿Quién está conectado?

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