我怎样才能检查一个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::first
和List::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::Util
或List::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"; } }
您可能需要检查数字前导空间的一致性