• Publicidad

Crear una subrutina con dos arrays

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

Crear una subrutina con dos arrays

Notapor abraham03 » 2017-12-14 20:05 @878

Hola, ¿qué tal?

Tengo este código que tiene dos listas asignadas como array y al comparar una con la otra extrae lo que dé un match.

Lo quiero en subrutina porque estoy haciendo otro script con diferentes variantes (múltiples opciones) pero al final siempre tengo dos listas (dos arrays) que se comparan.

Estoy tratando de hacer la subrutina desde que inicia el "for (@list){ hasta el final }" y usarla algo así como " extract (@lista, @nombres) ", y que imprima lo mismo que genera este código:

Barney Rubble
Wilma Flinstone


Realmente lo quiero para usarlo en análisis bioinformáticos (extracción de asignaciones taxonómicas con QIIME) pero tiene el mismo principio que el código que expongo aquí (se me hace más sencillo explicarlo así).

¡¡¡ Muchas Gracias !!!
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2.   use strict;
  3.   use warnings;
  4.   my @array1 = ( "Fred Flinstone", "Wilma Flinstone", "Barney Rubble", "Betty Rubble" );
  5.   my @list = ("Ba", "Wil");
  6.   my (@match, @extract);
  7.     for (@list){
  8.         my $unit = $_;
  9.         chomp $unit;
  10.           @match = grep (/$unit/, @array1);
  11.             foreach (@match){
  12.             push (@extract, $_);
  13.         }
  14.     }
  15.       foreach (@extract){
  16.     print "$_\n";
  17.   }
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4
abraham03
Perlero nuevo
Perlero nuevo
 
Mensajes: 19
Registrado: 2016-08-05 15:52 @703

Publicidad

Re: Crear una subrutina con dos arrays

Notapor explorer » 2017-12-16 23:15 @010

La siguiente versión realiza lo que quieres, usando una subrutina.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use v5.14;
  3. use strict;
  4. use warnings;
  5.  
  6. my @array1 = ( "Fred Flinstone", "Wilma Flinstone", "Barney Rubble", "Betty Rubble" );
  7. my @list   = ( "Ba", "Wil" );
  8.  
  9. my $extract_ref = busca(\@array1, \@list);
  10.  
  11. for (@$extract_ref) {
  12.     say;
  13. }
  14.  
  15. sub busca {
  16.     my ($lista1_ref, $lista2_ref) = @_;
  17.     my @extract;
  18.  
  19.     for my $unit (@$lista2_ref) {
  20.         push @extract, grep /$unit/, @$lista1_ref;
  21.     }
  22.  
  23.     return \@extract;
  24. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Como dices que quieres aplicarlo a Bioinformática, damos por supuesto que las cadenas a comparar pueden ser muy grandes. O quizás no. Vamos a suponer que sí. En ese caso, nos interesa que el programa no "mueva" esas cadenas entre la parte principal del programa y la subrutina. Usaremos la misma técnica que en otros lenguajes: en lugar de que los argumentos se pasen por valor, pasaremos una referencia a ese valor. Esto es algo muy rápido. No importa que la cadena de texto ocupe varios gigas. Solo pasamos una referencia, que ocupa muy poco.

En este caso concreto, a la subrutina le pasaremos una referencia a los dos arrays. De esa manera, no importa que los arrays tenga cuatro elementos o cuatro millones.

El paso de referencias como argumentos obliga a que accedamos a los elementos del array, desreferenciando. Por eso ves en el código los caracteres '@$' delante de los nombres de las variables.

El resultado es un array, pero en lugar de devolverlo tal cual, devolvemos una referencia. En la parte principal del programa lo recibimos y accedemos a los elementos de la misma manera que en la subrutina.

Con esto conseguimos una buena velocidad, pero se puede conseguir mucha más velocidad.

Dentro de la subrutina, estamos haciendo un bucle por todos los elementos de lista2. Por cada elemento de estos, lo comparamos con todos los elementos de lista1. Eso puede tardar mucho, si las listas son grandes (o con elementos muy grandes).

Una opción es la de convertir todos los elementos de lista2 en una única expresión regular:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use v5.14;
  3. use strict;
  4. use warnings;
  5.  
  6. my @array1 = ( "Fred Flinstone", "Wilma Flinstone", "Barney Rubble", "Betty Rubble" );
  7. my @list   = ( "Ba", "Wil" );
  8.  
  9. my $extract_ref = busca(\@array1, \@list);
  10.  
  11. say for @$extract_ref;
  12.  
  13. sub busca {
  14.     my ($lista1_ref, $lista2_ref) = @_;
  15.  
  16.     my $patron = join '|', @$lista2_ref;                # creación del patrón como un grupo de opciones: A | B | C
  17.  
  18.     my @extract = grep /$patron/, @$lista1_ref;         # filtramos todos los datos con el patrón
  19.  
  20.     return \@extract;
  21. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

La ventaja, además, es que el resultado contiene los elementos coincidentes en el mismo orden que están en lista1.

Edito: Esta versión usa la nueva sintaxis de desreferencia, y es un pelín más rápida.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use v5.20;
  3. use strict;
  4. use warnings;
  5.  
  6. my @array1 = ( "Fred Flinstone", "Wilma Flinstone", "Barney Rubble", "Betty Rubble" );
  7. my @list   = ( "Ba", "Wil" );
  8.  
  9. my $extract_ref = busca(\@array1, \@list);
  10.  
  11. say for $extract_ref->@*;
  12.  
  13. sub busca {
  14.     my ($lista1_ref, $lista2_ref) = @_;
  15.  
  16.     my $patron = join '|', $lista2_ref->@*;
  17.  
  18.     return [ grep /$patron/, $lista1_ref->@* ];
  19. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Crear una subrutina con dos arrays

Notapor abraham03 » 2017-12-18 21:35 @941

Muchísimas gracias, la verdad es que me ha ayudado mucho el código que hiciste, le hice unas pocas modificaciones y quedó bien adaptado, y gracias por los consejos. La verdad es que sí suelen ser archivos grandes en algunas ocasiones, es para Metagenómica, específicamente para extraer líneas de asignaciones taxonómicas a partir de un listado de referencias, es por ello que quería la sub para dos arrays; había hecho el match con un loop, pero como quiero agregarle más opciones ¡¡ no quería estar reescribiendo el mismo código una y otra vez !!

Muchas gracias.

Abraham.
abraham03
Perlero nuevo
Perlero nuevo
 
Mensajes: 19
Registrado: 2016-08-05 15:52 @703


Volver a Básico

¿Quién está conectado?

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

cron