【问题标题】:Perl: Good way to test if a value is in an array?Perl:测试值是否在数组中的好方法?
【发布时间】:2011-06-02 00:32:03
【问题描述】:

如果我有一个数组:

@int_array = (7,101,80,22,42);

如何在不遍历每个元素的情况下检查整数值 80 是否在数组中?

【问题讨论】:

标签: arrays perl


【解决方案1】:

你不能没有循环。这就是数组的意义所在。您可以使用 grep 或 smartmatch 使用隐式循环,但仍然存在循环。如果您想避免循环,请改用(或另外)使用哈希。

# grep
if ( grep $_ == 80, @int_array ) ...

# smartmatch
use 5.010001;
if ( 80 ~~ @int_array ) ...

在使用智能匹配之前,请注意:

http://search.cpan.org/dist/perl-5.18.0/pod/perldelta.pod#The_smartmatch_family_of_features_are_now_experimental:

smartmatch 系列功能现在处于试验阶段

智能匹配,在 v5.10.0 中添加并在 v5.10.1 中进行了重大修改,一直是投诉点。尽管它有很多有用的方式,但它也被证明对于 Perl 的用户和实现者来说是有问题的和令人困惑的。关于如何最好地解决这个问题,已经提出了许多建议。很明显,smartmatch 几乎肯定会在未来改变或消失。不建议依赖其当前行为。

现在,当解析器看到 ~~、given 或 when 时会发出警告。要禁用这些警告,您可以将此行添加到适当的范围

【讨论】:

  • ++。智能匹配(或者可能来自 List::MoreUtils 的东西)是要走的路。为了完整起见,以下是使用哈希的方法:my @array = 1..1000;我的 %hash; undef @hash{@array};说“10 在数组中!”如果存在 $hash{10};
  • undef @hash{@array} 有效,但实际上并没有记录这样做。请改用@hash{@array} = ()
  • 我不确定你的意思,所以我用谷歌搜索并找到了这个:perlmonks.org/?node_id=435223 谢谢你的更正,ysth!
  • grep 不好 - OP 明确表示“没有循环遍历每个元素”。
  • @DVK:这就是我说“你不能”的原因。 smartmatch 或其他任何东西就像 grep 或 for 一样是一个循环(可能会遍历每个元素)。
【解决方案2】:

CPAN 解决方案:使用List::MoreUtils

use List::MoreUtils qw{any}; 
print "found!\n" if any { $_ == 7 } (7,101,80,22,42);

如果您需要在同一个数组中进行 MANY MANY 查找,更有效的方法是将数组存储在哈希中一次,然后在哈希中查找:

@int_array{@int_array} = 1;
foreach my $lookup_value (@lookup_values) {
    print "found $lookup_value\n" if exists $int_array{$lookup_value}
}

为什么要使用此解决方案而不是替代方案?

  • 在 5.10 之前的 Perl 中无法使用智能匹配。根据 brian d foy]2 的这篇 SO 帖子,智能匹配是短路的,因此它与 5.10 的“任何”解决方案一样好。

  • grep 解决方案循环遍历 整个 列表,即使 1,000,000 个长列表的第一个元素匹配。 any 将在找到第一个匹配项时短路并退出,因此效率更高。原始海报明确表示“不遍历每个元素”

  • 如果您需要进行大量查找,哈希创建的一次性沉没成本使哈希查找方法比其他任何方法更有效。见this SO post for details

【讨论】:

  • 这取决于数组的大小和您需要进行的检查次数。解析外部模块的效率可能低于完整的grep
  • 另外,如果效率是主要关注点,只需手动操作foreachlast
  • @kemp - IIRC,List::MoreUtils 有一个 XS 实现。这比 foreach 和 last 快。如果使用本机 Perl,这可能就是该模块所做的。
【解决方案3】:

另一种检查数组中数字的方法:

#!/usr/bin/env perl

use strict;
use warnings;

use List::Util 'first';

my @int_array       = qw( 7 101 80 22 42 );
my $number_to_check = 80;

if ( first { $_ == $number_to_check } @int_array ) {
    print "$number_to_check exists in ", join ', ', @int_array;
}

List::Util

【讨论】:

    【解决方案4】:
    if ( grep /^80$/, @int_array ) {
        ...
    }
    

    【讨论】:

    • 一般使用 grep 并不是一个好主意;它会分配一个额外的列表来保存 grep 的结果,即使第一个元素是 80,它也会继续遍历数组。
    • @gab:在典型情况下,继续通过阵列比提前退出更便宜。
    • @gab:一般来说,我避免像 C 那样编写 Perl —— 如果 Perl 提供了方便的工具而我不使用它们,那为什么还要使用 Perl?
    • @kemp:我完全同意——但是,Perl 快速检查值是否存在的方法是使用散列而不是列表。
    • 哈希不能代替列表,否则我们根本不会使用列表
    【解决方案5】:

    如果您使用的是 Perl 5.10 或更高版本,则可以使用 smart match 运算符 ~~

    my $found = (80 ~~ $in_array);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多