如果要在数组中搜索单个标量,可以使用List::Util 的first 子例程。它一知道答案就停下来。我不认为这会比散列查找更快如果你已经有了散列,但是当你考虑创建散列并将它放在内存中时,你可能更方便只搜索你已经拥有的数组。
至于 smart-match 算子的聪明,如果你想看看它有多聪明,那就测试一下吧。 :)
您至少要检查三个案例。最坏的情况是你想找到的每个元素都在最后。最好的情况是您要查找的每个元素都位于开头。可能的情况是您要查找的元素平均位于中间。
现在,在我开始这个基准测试之前,我希望如果智能匹配可以短路(它可以;它记录在 perlsyn 中),那么尽管数组大小,最佳情况时间将保持不变,而其他的变得越来越糟。如果不能短路,每次都要扫描整个阵列,时间上应该没有区别,因为每个案例的工作量都是一样的。
这是一个基准:
#!perl
use 5.12.2;
use strict;
use warnings;
use Benchmark qw(cmpthese);
my @hits = qw(A B C);
my @base = qw(one two three four five six) x ( $ARGV[0] || 1 );
my @at_end = ( @base, @hits );
my @at_beginning = ( @hits, @base );
my @in_middle = @base;
splice @in_middle, int( @in_middle / 2 ), 0, @hits;
my @random = @base;
foreach my $item ( @hits ) {
my $index = int rand @random;
splice @random, $index, 0, $item;
}
sub count {
my( $hits, $candidates ) = @_;
my $count;
foreach ( @$hits ) { when( $candidates ) { $count++ } }
$count;
}
cmpthese(-5, {
hits_beginning => sub { my $count = count( \@hits, \@at_beginning ) },
hits_end => sub { my $count = count( \@hits, \@at_end ) },
hits_middle => sub { my $count = count( \@hits, \@in_middle ) },
hits_random => sub { my $count = count( \@hits, \@random ) },
control => sub { my $count = count( [], [] ) },
}
);
这是各个部分的作用。请注意,这是两个轴上的对数图,因此下降线的斜率并不像看起来那么接近:
所以,看起来智能匹配运算符有点聪明,但这并不能真正帮助您,因为您可能仍然需要扫描整个数组。您可能事先不知道在哪里可以找到您的元素。我希望散列的性能与最佳情况智能匹配相同,即使您必须为此放弃一些内存。
好的,智能乘以 2 的智能匹配很棒,但真正的问题是“我应该使用它吗?”。另一种方法是哈希查找,我一直在烦恼我没有考虑过这种情况。
与任何基准测试一样,在实际测试之前,我会先考虑结果可能是什么。我希望如果我已经有了哈希值,那么查找一个值会很快。那个案子没问题。我对我还没有哈希的情况更感兴趣。我可以多快使散列和查找成为键?我预计它的表现不会那么好,但它仍然比最坏情况下的智能匹配更好吗?
但是,在您查看基准之前,请记住,仅通过查看数字几乎没有足够的信息来说明您应该使用哪种技术。问题的上下文选择最好的技术,而不是最快的、无上下文的微基准。考虑几个可以选择不同技术的案例:
- 您有一个要重复搜索的数组
- 您总是会得到一个只需要搜索一次的新数组
- 您会得到非常大的数组,但内存有限
现在,牢记这些,我将添加到我之前的程序中:
my %old_hash = map {$_,1} @in_middle;
cmpthese(-5, {
...,
new_hash => sub {
my %h = map {$_,1} @in_middle;
my $count = 0;
foreach ( @hits ) { $count++ if exists $h{$_} }
$count;
},
old_hash => sub {
my $count = 0;
foreach ( @hits ) { $count++ if exists $old_hash{$_} }
$count;
},
control_hash => sub {
my $count = 0;
foreach ( @hits ) { $count++ }
$count;
},
}
);
这是情节。颜色有点难以区分。最低行是您必须在任何时候想要搜索它时创建散列的情况。可以说是很可怜了。最高的两行(绿色)是散列控制(实际上没有散列)和现有散列查找。这是一个日志/日志图;这两种情况甚至比智能匹配控制(只调用一个子程序)还要快。
还有一些其他的事情需要注意。 “随机”案例的行有点不同。这是可以理解的,因为每个基准测试(因此,每个数组规模运行一次)将命中元素随机放置在候选数组中。有些运行将它们放早一点,有些放慢一点,但是由于我每次运行整个程序时只创建一次@random 数组,因此它们会移动一点。这意味着生产线上的颠簸并不重要。如果我尝试了所有位置并取平均值,我希望“随机”线与“中间”线相同。
现在,看看这些结果,我想说智能匹配在最坏的情况下比哈希查找在最坏的情况下要快得多。那讲得通。要创建散列,我必须访问数组的每个元素并生成散列,这需要大量复制。智能匹配没有复制。
这里还有一个我不会研究的案例。哈希何时变得比智能匹配更好?也就是说,什么时候创建哈希的开销在重复搜索中分散得足够多,以至于哈希是更好的选择?