我怎样才能检查一个Perl数组是否包含一个特定的值?

我试图找出一种方法来检查数组中的值的存在,而不是遍历数组。

我正在读取一个参数的文件。 我有一长串的参数,我不想处理。 我把这些不需要的参数放在一个数组@badparams

我想读一个新的参数,如果它不存在于@badparams ,处理它。 如果它存在于@badparams ,请转到下一个阅读。

简单地把数组变成一个哈希值:

 my %params = map { $_ => 1 } @badparams; if(exists($params{$someparam})) { ... } 

您也可以添加更多(唯一)参数到列表中:

 $params{$newparam} = 1; 

后来得到一个(独特的)参数列表:

 @badparams = keys %params; 

最好的通用目的 – 特别是短arrays(1000个或更less)和编码器,他们不确定哪些优化最适合他们的需求。

 # $value can be any regex. be safe if ( grep( /^$value$/, @array ) ) { print "found it"; } 

已经提到,即使数组中的第一个值匹配,grep也会传递所有的值。 这是真的,但是在大多数情况下grep仍然是非常快的 。 如果你正在谈论短arrays(less于1000个项目),那么大多数algorithm将会非常快。 如果你谈论的是非常长的数组(1,000,000个项目),grep可以快速接受,无论项目是数组中的第一个还是最后一个。

较长数组的优化案例:

如果您的数组已sorting ,请使用“二分查找”。

如果多次重复search同一个数组 ,则首先将其复制到散列中,然后检查散列。 如果内存是一个问题,然后将每个项目从数组中移动到散列。 更高的内存效率,但破坏了原始数组。

如果在数组中重复search相同的值 ,则懒惰地构build一个caching。 (在search每个项目时,首先检查search结果是否存储在持久散列中,如果在散列中找不到search结果,则search数组并将结果放入持久散列中,以便下一次在散列中find它并跳过search)。

注意:在处理长数组时,这些优化只会更快。 不要过度优化。

您可以使用Perl 5.10中的 smartmatchfunction,如下所示:

对于下面的字面值查找将做的伎俩。

 if ( "value" ~~ @array ) 

对于标量查找,下面的操作将如上所述。

 if ($val ~~ @array) 

对于下面的内联数组,如上所述。

 if ( $var ~~ ['bar', 'value', 'foo'] ) 

Perl 5.18中, smartmatch被标记为实验,因此您需要通过在脚本/模块中添加以下代码来打开实验杂注来closures警告:

 use experimental 'smartmatch'; 

或者,如果你想避免使用smartmatch – 那么作为亚伦说使用:

 if ( grep( /^$value$/, @array ) ) { #TODO: } 

这篇博文讨论了这个问题的最佳答案。

作为一个简短的总结,如果你可以安装CPAN模块,那么最可读的解决scheme是:

 any(@ingredients) eq 'flour'; 

要么

 @ingredients->contains('flour'); 

然而,更常见的成语是:

 any { $_ eq 'flour' } @ingredients 

但请不要使用第first()函数! 它根本不expression你的代码的意图。 不要使用~~ “智能匹配”操作符:它已经损坏。 不要使用grep()也不要使用哈希方法:遍历整个列表。

any()将停止,只要它发现你的价值。

查看博客文章了解更多详情。

尽pipe使用起来很方便,但转换为散列解决scheme似乎花费了相当多的性能,这对我来说是一个问题。

 #!/usr/bin/perl use Benchmark; my @list; for (1..10_000) { push @list, $_; } timethese(10000, { 'grep' => sub { if ( grep(/^5000$/o, @list) ) { # code } }, 'hash' => sub { my %params = map { $_ => 1 } @list; if ( exists($params{5000}) ) { # code } }, }); 

基准testing的输出:

 Benchmark: timing 10000 iterations of grep, hash... grep: 8 wallclock secs ( 7.95 usr + 0.00 sys = 7.95 CPU) @ 1257.86/s (n=10000) hash: 50 wallclock secs (49.68 usr + 0.01 sys = 49.69 CPU) @ 201.25/s (n=10000) 

@ eakssjo的基准已经被打破 – 在循环中创build散列值与在循环中创build正则expression式。 固定版本(加上我已经添加List::Util::firstList::MoreUtils::any ):

 use List::Util qw(first); use List::MoreUtils qw(any); use Benchmark; my @list = ( 1..10_000 ); my $hit = 5_000; my $hit_regex = qr/^$hit$/; # precompute regex my %params; $params{$_} = 1 for @list; # precompute hash timethese( 100_000, { 'any' => sub { die unless ( any { $hit_regex } @list ); }, 'first' => sub { die unless ( first { $hit_regex } @list ); }, 'grep' => sub { die unless ( grep { $hit_regex } @list ); }, 'hash' => sub { die unless ( $params{$hit} ); }, }); 

结果(这是100_000迭代,比@ eakssjo的答案多10倍):

 Benchmark: timing 100000 iterations of any, first, grep, hash... any: 0 wallclock secs ( 0.67 usr + 0.00 sys = 0.67 CPU) @ 149253.73/s (n=100000) first: 1 wallclock secs ( 0.63 usr + 0.01 sys = 0.64 CPU) @ 156250.00/s (n=100000) grep: 42 wallclock secs (41.95 usr + 0.08 sys = 42.03 CPU) @ 2379.25/s (n=100000) hash: 0 wallclock secs ( 0.01 usr + 0.00 sys = 0.01 CPU) @ 10000000.00/s (n=100000) (warning: too few iterations for a reliable count) 

你当然想要一个哈希在这里。 将不良参数作为关键字放在哈希中,然后决定哈希中是否存在特定的参数。

 our %bad_params = map { $_ => 1 } qw(badparam1 badparam2 badparam3) if ($bad_params{$new_param}) { print "That is a bad parameter\n"; } 

如果你真的有兴趣做一个数组,看看List::UtilList::MoreUtils

方法1:grep(可能要小心,而值预计是一个正则expression式)。

如果查看资源,尽量避免使用grep

 if ( grep( /^$value$/, @badparams ) ) { print "found"; } 

方法2:线性search

 for (@badparams) { if ($_ eq $value) { print "found"; } } 

方法3:使用散列

 my %hash = map {$_ => 1} @badparams; print "found" if (exists $hash{$value}); 

方法4:smartmatch

(在Perl 5.10中添加,在Perl 5.18中标记为实验)。

 use experimental 'smartmatch'; # for perl 5.18 print "found" if ($value ~~ @badparams); 

方法5:使用核心模块List::MoreUtils

 use List::MoreUtils qw(any uniq);; @badparams = (1,2,3); $value = 1; print "found" if any {$_ eq $value} @badparams; 

有两种方法可以做到这一点。 正如其他post所build议的那样,您可以使用将值引入查找表的散列值。 (我会再加一个成语)

 my %bad_param_lookup; @bad_param_lookup{ @bad_params } = ( 1 ) x @bad_params; 

但是,如果它主要是单词字符的数据而不是太多的元数据,则可以将其转储为正则expression式:

 use English qw<$LIST_SEPARATOR>; my $regex_str = do { local $LIST_SEPARATOR = '|'; "(?:@bad_params)"; }; # $front_delim and $back_delim being any characters that come before and after. my $regex = qr/$front_delim$regex_str$back_delim/; 

这个解决scheme将不得不针对您要查找的“错误值”types进行调整。 再说一遍,对于某些types的string可能是完全不合适的,所以要注意空格

 my @badparams = (1,2,5,7,'a','zzz'); my $badparams = join('|',@badparams); # '|' or any other character not present in params foreach my $par (4,5,6,7,'a','z','zzz') { if ($badparams =~ /\b$par\b/) { print "$par is present\n"; } else { print "$par is not present\n"; } } 

您可能需要检查数字前导空间的一致性