




my %k;
map { $k{$_} = 1 } @mylist1;
map { $k{$_} = 1 } @mylist2;
@mylist2 = keys %k;
Альтернативно:
my %k;
map { $k{$_} = 1 } @mylist2;
push(@mylist2, grep { !exists $k{$_} } @mylist1);
На самом деле - они могут быть неправильными, потому что они не учитывают, могут ли дубликаты существовать в любом из исходных списков.
Вы не сказали в своем вопросе, должны ли списки представлять наборы (которые не могут содержать дубликатов) или просто списки. То, что вы действительно хотите @mylist2 = @mylist1 U @mylist2, предполагает, что вы рассматриваете их как наборы.
Обновлено: изменено приращение для назначения - сохраняет чтение хеш-значения
Второй вариант - самый быстрый по моим измерениям - и быстрее, чем метод uniq в List :: MoreUtils.
[Оригинальный ответ от 27.11.2008 до «С вопроса»; анализ с этого момента является новым по состоянию на 29 ноября 2008 года.]
Самый быстрый - не уверен. Это работает, хотя и некрасиво:
#!/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";
Это позволяет избежать преобразования массивов в хэши, но для больших массивов вспомогательная функция value_in может работать медленно.
Поскольку вопрос был в том, «какой метод самый быстрый», я провел несколько тестов. К моему не слишком большому удивлению, мой метод оказался самым медленным. К моему удивлению, самый быстрый метод был не из List :: MoreUtils. Вот тестовый код и результаты - с использованием модифицированной версии моего первоначального предложения.
#!/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:
Это Perl 5.10.0, скомпилированный для 32-битного SPARC с множественностью на старинном Sun E450 под управлением Solaris 10.
Я считаю, что тестовые установки правильные; все они генерируют свой ответ в новый массив, отдельный от mylist1 и mylist2 (так что mylist1 и mylist2 могут быть повторно использованы для следующего теста). Ответ, обозначенный как HV1 (значения хэша 1), имеет начало отсчета времени после присвоения @ mylist5, что я считаю правильным. Однако, когда я рассчитал время со старта перед назначением, он все равно был самым быстрым:
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:
Из-за вашего комментария «(ABCDE)» я предполагаю, что вы на самом деле имели в виду поместить в mylist1 те элементы в mylist2, которых нет в mylist1. Если это предположение неверно, вам нужно сказать что-то о том, в каком порядке вы хотите, чтобы все закончилось.
Сначала сохраните в хэше, какие элементы находятся в mylist1, затем поместите все элементы в mylist2, не найденные в хеше, в mylist1.
my %in_mylist1;
@in_mylist1{@mylist1} = ();
push @mylist1, grep ! exists $in_mylist1{$_}, @mylist2;
Вы можете просто использовать List::MoreUtils модуля uniq:
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
Умение определять и использовать модули - довольно важная часть изучения Perl.
Конечно, но вы все равно должны знать основы
Я бы хотел, чтобы больше людей использовали List :: MoreUtils вместо того, чтобы писать одни и те же маленькие кусочки кода снова и снова.
my(%work);
@work{@mylist1, @mylist2} = undef;
@mylist2 = sort keys %work;
Если дубликаты разрешены в mylist2 (а я не вижу причин, по которым они бы не были), то это решение удаляет их.
Это нормально, если вам не нужно сохранять исходный порядок.