2008-11-27 14 views

Respuesta

12
my %k; 
map { $k{$_} = 1 } @mylist1; 
map { $k{$_} = 1 } @mylist2; 
@mylist2 = keys %k; 

alternativa:

my %k; 
map { $k{$_} = 1 } @mylist2; 
push(@mylist2, grep { !exists $k{$_} } @mylist1); 

En realidad - estos pueden estar equivocados, ya que no tienen en cuenta si los duplicados podrían existir en cualquiera de las listas originales.

No dijo en su pregunta si se supone que las listas representan conjuntos (que no pueden contener duplicados) o simplemente listas simples. Si realmente quiere @mylist2 = @mylist1 U @mylist2, sugiere que los está tratando como conjuntos.

EDIT: Valor mínimo para asignar cambiado - guarda una lectura del valor hash

+0

Esto está bien si no es necesario para mantener el orden original. –

+1

La segunda opción es la más rápida según mis mediciones, y más rápida que el método uniq en List :: MoreUtils. –

2

[Respuesta original a partir de 2008-11-27 hasta "Dado que la pregunta"; el análisis de allí en adelante es nuevo a partir de 2008-11-29.]

Lo más rápido - no estoy seguro. Esto funciona, aunque no es bastante:

#!/bin/perl -w 
use strict; 

my @mylist1; 
push(@mylist1,"A"); 
push(@mylist1,"B"); 
push(@mylist1,"C"); 

my @mylist2; 
push(@mylist2,"A"); 
push(@mylist2,"D"); 
push(@mylist2,"E"); 

sub value_in 
{ 
    my($value, @array) = @_; 
    foreach my $element (@array) 
    { 
     return 1 if $value eq $element; 
    } 
    return 0; 
} 

@mylist2 = (@mylist2, grep { ! value_in($_, @mylist2) } @mylist1); 

print sort @mylist2, "\n"; 

Esto evita la conversión de las matrices en los hashes - pero para las grandes matrices, la sub value_in puede ser lento.

Como la pregunta era "¿cuál es el método más rápido?", Realicé algunas evaluaciones comparativas. Para mi sorpresa no demasiado vasta, mi método fue más lento. Para mi sorpresa, el método más rápido no fue de List :: MoreUtils. Aquí está el código de prueba y los resultados, usando una versión modificada de mi propuesta original.

#!/bin/perl -w 
use strict; 
use List::MoreUtils qw(uniq); 
use Benchmark::Timer; 

my @mylist1; 
push(@mylist1,"A"); 
push(@mylist1,"B"); 
push(@mylist1,"C"); 

my @mylist2; 
push(@mylist2,"A"); 
push(@mylist2,"D"); 
push(@mylist2,"E"); 

sub value_in 
{ 
    my($value) = shift @_; 
    return grep { $value eq $_ } @_; 
} 

my @mylist3; 
my @mylist4; 
my @mylist5; 
my @mylist6; 

my $t = Benchmark::Timer->new(skip=>1); 
my $iterations = 10000; 

for my $i (1..$iterations) 
{ 
    $t->start('JLv2'); 
    @mylist3 = (@mylist2, grep { ! value_in($_, @mylist2) } @mylist1); 
    $t->stop('JLv2'); 
} 
print $t->report('JLv2'); 

for my $i (1..$iterations) 
{ 
    $t->start('LMU'); 
    @mylist4 = uniq(@mylist1, @mylist2); 
    $t->stop('LMU'); 
} 
print $t->report('LMU'); 

for my $i (1..$iterations) 
{ 
    @mylist5 = @mylist2; 
    $t->start('HV1'); 
    my %k; 
    map { $k{$_} = 1 } @mylist5; 
    push(@mylist5, grep { !exists $k{$_} } @mylist1); 
    $t->stop('HV1'); 
} 
print $t->report('HV1'); 

for my $i (1..$iterations) 
{ 
    $t->start('HV2'); 
    my %k; 
    map { $k{$_} = 1 } @mylist1; 
    map { $k{$_} = 1 } @mylist2; 
    @mylist6 = keys %k; 
    $t->stop('HV2'); 
} 
print $t->report('HV2'); 


print sort(@mylist3), "\n"; 
print sort(@mylist4), "\n"; 
print sort(@mylist5), "\n"; 
print sort(@mylist6), "\n"; 

Black JL: perl xxx.pl 
9999 trials of JLv2 (1.298s total), 129us/trial 
9999 trials of LMU (968.176ms total), 96us/trial 
9999 trials of HV1 (516.799ms total), 51us/trial 
9999 trials of HV2 (768.073ms total), 76us/trial 
ABCDE 
ABCDE 
ABCDE 
ABCDE 
Black JL: 

Esta es Perl 5.10.0 compilado para SPARC de 32 bits con multiplicidad en un antiguo E450 Sun con Solaris 10.

Creo que las configuraciones de prueba son justos; todos ellos generan su respuesta en una nueva matriz, separada de mylist1 y mylist2 (por lo que mylist1 y mylist2 pueden reutilizarse para la próxima prueba). La respuesta designada HV1 (valores de hash 1) tiene el inicio de temporización después de la asignación a @ mylist5, que creo que es correcta. Sin embargo, cuando hice la sincronización con el inicio antes de la asignación, que era todavía más rápida:

Black JL: perl xxx.pl 
9999 trials of JLv2 (1.293s total), 129us/trial 
9999 trials of LMU (938.504ms total), 93us/trial 
9999 trials of HV1 (505.998ms total), 50us/trial 
9999 trials of HV2 (756.722ms total), 75us/trial 
ABCDE 
ABCDE 
ABCDE 
ABCDE 
9999 trials of HV1A (655.582ms total), 65us/trial 
Black JL: 
1

Debido a su "(ABCDE)" comentario, estoy suponiendo que realmente significa empuje hacia mylist1 esos elementos en mylist2 que no están en mylist1. Si esta suposición es incorrecta, debe decir algo sobre en qué orden desea que terminen las cosas.

Primero, almacene qué elementos están en mylist1 en un hash, luego inserte todos los de mylist2 no encontrados en el hash en mylist1.

my %in_mylist1; 
@in_mylist1{@mylist1} =(); 
push @mylist1, grep ! exists $in_mylist1{$_}, @mylist2; 
23

Se podía utilizar de uniq el módulo List::MoreUtils:

use List::MoreUtils qw(uniq); 

my @mylist1; 
push(@mylist1, "A"); 
push(@mylist1, "B"); 
push(@mylist1, "C"); 

my @mylist2; 
push(@mylist2, "A"); 
push(@mylist2, "D"); 
push(@mylist2, "E"); 

@mylist2 = uniq(@mylist1, @mylist2); 

printf "%s\n", (join ',', @mylist2); # A,B,C,D,E 
+0

bien, eso funcionará, pero no es forma de aprender Perl ... – Alnitak

+3

Aprender a identificar y usar módulos es una parte muy importante del aprendizaje perl. – oeuftete

+0

Claro, pero aún debe conocer los fundamentos – Alnitak

0
my(%work); 
@work{@mylist1, @mylist2} = undef; 
@mylist2 = sort keys %work; 
+0

Si se permiten duplicados en mylist2 (y no veo ninguna razón por la que no lo sean), esta solución los elimina. – noswonky

Cuestiones relacionadas