如何检查Perl数组是否包含特定值?

Mel*_*Mel 226 arrays perl comparison

我试图找出一种方法来检查数组中是否存在值而不迭代数组.

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

我想读一个新参数,如果它不存在@badparams,请处理它.如果确实存在@badparams,请转到下一个读取.

Aar*_*ris 211

最佳通用 - 特别是短阵列(1000个或更少项目)和编码器,不确定哪种优化最适合他们的需求.

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

已经提到,即使数组中的第一个值匹配,grep也会遍历所有值.这是事实,但对于大多数情况来说,grep仍然非常快.如果你在谈论短阵列(少于1000个项目),那么大多数算法都会非常快.如果您正在讨论非常长的数组(1,000,000个项目),那么无论该项目是数组中的第一个,中间还是最后一个,grep都是可以接受的.

更长阵列的优化案例:

如果您的数组已排序,请使用"二进制搜索".

如果多次重复搜索相同的数组,请先将其复制到哈希中,然后检查哈希值.如果内存是一个问题,那么将每个项目从数组移动到哈希.内存效率更高但会破坏原始阵列.

如果在数组中重复搜索相同的值,则懒惰地构建缓存.(当搜索每个项目时,首先检查搜索结果是否存储在持久化哈希中.如果在哈希中找不到搜索结果,则搜索数组并将结果放入持久化哈希中,以便下次我们将在哈希中找到它并跳过搜索).

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

  • if("value"~~ @ array)是正确的答案 (27认同)
  • @DennisWilliamson ...... [在5.18它被认为是实验](https://metacpan.org/pod/release/RJBS/perl-5.18.0/pod/perldelta.pod#The-smartmatch-family-of-features-是,现在已经实验). (15认同)
  • 在Perl 5.10中引入了双波浪号 (12认同)
  • 不要使用if("value"~~ @ array).~~是一个名为Smartmatch的实验性功能.该实验似乎被视为失败,将在未来的Perl版本中删除或修改. (7认同)
  • 在生产代码中避免使用smartmatch.这是不稳定/实验性的,有待进一步通知. (5认同)
  • 如果您不确定“$value”的内容,那么使用“/^$value$/”是有问题的——如果存在正则表达式元字符(例如,只是一个左括号),那么调用可能会失败。要么写成`/^\Q$value\E$/`,要么完全避免使用正则表达式,使用`eq`(不幸的是可读性稍差):`grep { $_ eq $value } ...`。 (2认同)

jkr*_*mer 182

只需将数组转换为哈希:

my %params = map { $_ => 1 } @badparams;

if(exists($params{$someparam})) { ... }
Run Code Online (Sandbox Code Playgroud)

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

$params{$newparam} = 1;
Run Code Online (Sandbox Code Playgroud)

然后得到一个(唯一的)参数列表:

@badparams = keys %params;
Run Code Online (Sandbox Code Playgroud)

  • 对于记录,此代码仍然会遍历数组.map {}调用只是使迭代很容易输入. (36认同)
  • 如果@badparams中的值是伪静态的并且您打算对地图进行大量检查,我只会这样做.我建议不要一次性检查. (3认同)
  • @RobWells不,它会正常工作.下次看到相同的值时,它只会覆盖哈希中的条目,在这种情况下,它会再次将其设置为"1". (3认同)

Bit*_*map 117

您可以在Perl 5.10中使用smartmatch功能,如下所示:

对于字面值查找,下面的操作就可以了.

if ( "value" ~~ @array ) 
Run Code Online (Sandbox Code Playgroud)

对于标量查找,执行以下操作将如上所述.

if ($val ~~ @array)
Run Code Online (Sandbox Code Playgroud)

对于下面的内联数组,将按上述方式工作.

if ( $var ~~ ['bar', 'value', 'foo'] ) 
Run Code Online (Sandbox Code Playgroud)

Perl 5.18中, smartmatch被标记为实验性的,因此您需要通过在下面添加脚本/模块来打开实验性编译指示来关闭警告:

use experimental 'smartmatch';
Run Code Online (Sandbox Code Playgroud)

或者,如果你想避免使用smartmatch - 那么就像Aaron所说:

if ( grep( /^$value$/, @array ) ) {
  #TODO:
}
Run Code Online (Sandbox Code Playgroud)

  • **警告:**你可能想要避免这个,因为操作员在不同版本中的行为明显不同,并且同时[标记为实验](https://metacpan.org/pod/release/RJBS /perl-5.17.11/pod/perldelta.pod#Incompatible-Changes).因此,除非您完全控制您的perl版本(以及谁拥有它),否则您应该避免使用它. (17认同)
  • 这很好,但似乎是Perl 5.10的新功能.在我弄清楚为什么我会遇到语法错误之前花了一些时间. (4认同)

mas*_*cip 43

这篇博客文章讨论了这个问题的最佳答案.

作为简短摘要,如果您可以安装CPAN模块,那么最易读的解决方案是:

any(@ingredients) eq 'flour';
Run Code Online (Sandbox Code Playgroud)

要么

@ingredients->contains('flour');
Run Code Online (Sandbox Code Playgroud)

然而,更常见的习语是:

any { $_ eq 'flour' } @ingredients
Run Code Online (Sandbox Code Playgroud)

但请不要使用该first()功能!它根本不表达您的代码的意图.不要使用~~"智能匹配"操作符:它已损坏.并且不要使用grep()带有哈希的解决方案:它们遍历整个列表.

any() 一旦找到你的价值就会停止.

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

  • _ [any](http://perldoc.perl.org/List/Util.html#any)_需要`使用List :: Util qw(any);`.`List :: Util`在[核心模块]中(http://perldoc.perl.org/index-modules-L.html). (8认同)

aks*_*sel 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
            }
        },
});
Run Code Online (Sandbox Code Playgroud)

基准测试输出:

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)
Run Code Online (Sandbox Code Playgroud)

  • 使用[`List :: Util :: first`](http://perldoc.perl.org/List/Util.html)会更快,因为它在找到匹配时停止迭代. (5认同)
  • @Xaerxess:在我的情况下,我想做_one_ lookup,所以我认为计算hash/regex创建和lookup/grep都是公平的.它的任务是进行多次查找,我猜你的解决方案更好. (4认同)
  • 如果你只想进行一次迭代,那么你所选择的任何方法之间的区别是无法区分的,所以任何基准测试都是错误的,因为在这种情况下它是一种邪恶的微观优化. (3认同)
  • -1 你的基准测试有缺陷,`grep` 比创建散列和查找慢**显着**,因为前者是 O(n) 而后者是 O(1)。只需进行一次哈希创建(循环外)并预先计算正则表达式以仅测量方法([参见我的答案](http://stackoverflow.com/a/13939491/708434))。 (2认同)
  • 正则表达式只编译一次,因为它有标志"o". (2认同)

Xae*_*ess 11

@ eakssjo的基准测试被破坏 - 测量循环中的哈希与在循环中创建正则表达式.修正版(加我加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} );
        },
    });
Run Code Online (Sandbox Code Playgroud)

结果(这是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)
Run Code Online (Sandbox Code Playgroud)

  • 如果要测试多个元素,则提前创建哈希可以节省时间.但是如果你只想知道它是否包含单个元素,那么你就没有哈希了.因此,创建哈希应该是计算时间的一部分.对于正则表达式更是如此:对于您要查找的每个元素,您需要一个新的正则表达式. (6认同)

Kam*_*yan 8

方法1:grep(可能小心,而值预计是正则表达式).

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

if ( grep( /^$value$/, @badparams ) ) {
  print "found";
}
Run Code Online (Sandbox Code Playgroud)

方法2:线性搜索

for (@badparams) {
    if ($_ eq $value) {
       print "found";
    }
}
Run Code Online (Sandbox Code Playgroud)

方法3:使用哈希

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

方法4:smartmatch

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

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

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

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