我试图找出一种检查数组中是否存在值而不迭代数组的方法。
我正在读取参数的文件。我有一长串我不想处理的参数。我将这些不需要的参数放在数组@badparams
中。
我想阅读一个新参数,如果@badparams
中不存在,请对其进行处理。如果@badparams
中确实存在,请转到下一个阅读。
答案 0 :(得分:206)
最佳通用目的 - 特别是短阵列(1000个或更少项目)和编码器,不确定哪种优化最适合他们的需求。
# $value can be any regex. be safe
if ( grep( /^$value$/, @array ) ) {
print "found it";
}
有人提到,即使数组中的第一个值匹配,grep也会遍历所有值。这是事实,但是对于大多数情况来说, grep仍然非常快。如果你在谈论短阵列(少于1000个项目),那么大多数算法都会非常快。如果您正在谈论非常长的数组(1,000,000个项目),无论该项目是数组中的第一个还是中间或最后一个,grep都是可以接受的快速。
更长阵列的优化案例:
如果您的数组已排序,请使用“二进制搜索”。
如果重复搜索相同的数组,首先将其复制到哈希中,然后检查哈希值。如果内存是一个问题,那么将每个项目从数组移动到哈希。内存效率更高但会破坏原始阵列。
如果在数组中重复搜索相同的值,则懒惰地构建缓存。 (在搜索每个项目时,首先检查搜索结果是否存储在持久化哈希中。如果在哈希中找不到搜索结果,则搜索数组并将结果放入持久化哈希中,以便下次我们将在哈希中找到它并跳过搜索。)
注意:处理长数组时,这些优化只会更快。不要过度优化。
答案 1 :(得分:177)
只需将数组转换为哈希:
my %params = map { $_ => 1 } @badparams;
if(exists($params{$someparam})) { ... }
您还可以在列表中添加更多(唯一)参数:
$params{$newparam} = 1;
后来得到一份(独特的)参数列表:
@badparams = keys %params;
答案 2 :(得分:114)
您可以在 Perl 5.10 中使用smartmatch功能,如下所示:
对于字面值查找,执行以下操作即可。
if ( "value" ~~ @array )
对于标量查找,执行以下操作将如上所述。
if ($val ~~ @array)
对于下面的内联数组,将按上述方式工作。
if ( $var ~~ ['bar', 'value', 'foo'] )
在 Perl 5.18 中,smartmatch被标记为实验性,因此您需要通过在脚本/模块下面添加以下experimental编译指示来关闭警告:
use experimental 'smartmatch';
或者如果你想避免使用smartmatch - 那么就像Aaron所说:
if ( grep( /^$value$/, @array ) ) {
#TODO:
}
答案 3 :(得分:42)
This blog post讨论了这个问题的最佳答案。
作为简短摘要,如果您可以安装CPAN模块,那么最易读的解决方案是:
any(@ingredients) eq 'flour';
或
@ingredients->contains('flour');
然而,更常见的习语是:
any { $_ eq 'flour' } @ingredients
但请不要使用first()
功能!它根本不表达您的代码的意图。不要使用~~
“智能匹配”操作符:它已损坏。并且不要使用grep()
或使用散列的解决方案:它们遍历整个列表。
any()
会在找到您的价值后立即停止。
查看博客文章了解更多详情。
答案 4 :(得分:11)
尽管使用起来很方便,但转换为哈希的解决方案似乎需要相当多的性能,这对我来说是一个问题。
#!/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
}
},
});
基准测试的输出:
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)
答案 5 :(得分:10)
@eakssjo's benchmark已被破坏 - 测量循环中的哈希与在循环中创建正则表达式。固定版本(加上我添加了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答案的十倍):
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)
答案 6 :(得分:5)
如果查看资源,请尽量避免使用grep
。
if ( grep( /^$value$/, @badparams ) ) {
print "found";
}
for (@badparams) {
if ($_ eq $value) {
print "found";
}
}
my %hash = map {$_ => 1} @badparams;
print "found" if (exists $hash{$value});
(在Perl 5.10中添加,在Perl 5.18中标记为实验性的。)
use experimental 'smartmatch'; # for perl 5.18
print "found" if ($value ~~ @badparams);
List::MoreUtils
use List::MoreUtils qw(any uniq);;
@badparams = (1,2,3);
$value = 1;
print "found" if any {$_ eq $value} @badparams;
答案 7 :(得分:2)
你当然想要哈希。将坏参数作为键放在哈希中,然后确定哈希中是否存在特定参数。
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
答案 8 :(得分:2)
@files是现有数组
my @new_values = grep(/^2[\d].[\d][A-za-z]?/,@files);
print join("\n", @new_values);
print "\n";
/ ^ 2 [\ d]。[\ d] [A-za-z]?/ = vaues从2开始,你可以把任何正则表达式
答案 9 :(得分:1)
如果您需要了解数组中每个元素的数量,除了该元素的存在之外,还可以使用
my %bad_param_lookup;
@bad_param_lookup{ @bad_params } = ( 1 ) x @bad_params;
%bad_param_lookup = map { $_ => $bad_param_lookup{$_}++} @bad_params;
,然后对于@bad_params中的每个$ i,$ bad_param_lookup {$ i}都包含@bad_params中的$ i
答案 10 :(得分:0)
有两种方法可以做到这一点。您可以使用将值抛出到查找表的哈希中,如其他帖子所建议的那样。 (我只会添加另一个成语。)
my %bad_param_lookup;
@bad_param_lookup{ @bad_params } = ( 1 ) x @bad_params;
但是如果它的主要是单词字符的数据并且没有太多的元数据,你可以将其转换为正则表达式替换:
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/;
此解决方案必须针对您正在寻找的“坏值”类型进行调整。同样,它可能完全不适合某些类型的字符串,所以警告emptor 。
答案 11 :(得分:-1)
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";
}
}
您可能想要检查数字前导空格一致性