假设:
my @mylist1;
push(@mylist1,"A");
push(@mylist1,"B");
push(@mylist1,"C");
my @mylist2;
push(@mylist2,"A");
push(@mylist2,"D");
push(@mylist2,"E");
Perl中最快的方式是在mylist2中插入mylist1中的所有元素,而不是mylist2中的所有元素(ABCDE)。
答案 0 :(得分:23)
您可以使用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
答案 1 :(得分:12)
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
表示您将它们视为集合。
编辑:将增量更改为分配 - 保存读取哈希值
答案 2 :(得分:2)
[截至2008-11-27的原始答案为“问题以来”;从那时开始的分析是新的,截至2008-11-29。]
最快 - 不确定。这有效,虽然它不漂亮:
#!/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
sub可能会很慢。
由于问题是“什么是最快的方法”,我做了一些基准测试。令我惊讶的是,我的方法速度最慢。令我惊讶的是,最快的方法不是来自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:
这是针对32位SPARC编译的Perl 5.10.0,在运行Solaris 10的古董Sun E450上具有多重性。
我相信测试设置是公平的;他们都将自己的答案生成一个新的数组,与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:
答案 3 :(得分:1)
由于您的“(ABCDE)”评论,我假设您实际上意味着将mylist1中那些不在mylist1中的元素推送到mylist1。如果这个假设不正确,你需要说一下你希望事情最终结束的顺序。
首先,在散列中存储mylist1中的哪些元素,然后将散列中未找到的mylist2中的所有元素推送到mylist1。
my %in_mylist1;
@in_mylist1{@mylist1} = ();
push @mylist1, grep ! exists $in_mylist1{$_}, @mylist2;
答案 4 :(得分:0)
my(%work);
@work{@mylist1, @mylist2} = undef;
@mylist2 = sort keys %work;