21

我有一个可能值的列表:

@a = qw(foo bar baz);

如何以简洁的方式检查值$val是否存在@a

一个明显的实现是遍历列表,但我确信TMTOWTDI


感谢所有回答的人!我想强调的三个答案是:

  1. 公认的答案 - 最“内置”和向后兼容的方式。

  2. RET 的答案是最干净的,但只适用于 Perl 5.10 及更高版本。

  3. draegtun 的答案(可能)要快一些,但需要使用额外的模块。如果我可以避免它们,我不喜欢添加依赖项,在这种情况下不需要性能差异,但如果你有一个 1,000,000 元素列表,你可能想试试这个答案。

4

8 回答 8

40

如果您有 perl 5.10,请使用智能匹配运算符~~

print "Exist\n" if $var ~~ @array;

这几乎是魔术。

于 2009-04-06T07:50:13.087 回答
20

Perl 在 grep() 函数中的bulit 就是为此而设计的。

@matches = grep( /^MyItem$/, @someArray ); 

或者您可以将任何表达式插入匹配器

@matches = grep( $_ == $val, @a ); 
于 2009-04-06T07:46:27.497 回答
18

这在perlfaq4“如何判断某个元素是否包含在列表或数组中?”的回答中得到了回答。.

要搜索 perlfaq,您可以使用您喜欢的浏览器搜索perlfaq中所有问题的列表。

在命令行中,您可以使用 -q 开关切换到 perldoc 来搜索关键字。您会通过搜索“列表”找到答案:

perldoc -q list

(此答案的部分内容由 Anno Siegel 和 brian d foy 提供)

听到“in”这个词表明您可能应该使用散列而不是列表或数组来存储数据。哈希旨在快速有效地回答这个问题。数组不是。

话虽如此,有几种方法可以解决这个问题。在 Perl 5.10 及更高版本中,您可以使用智能匹配运算符来检查项目是否包含在数组或哈希中:

use 5.010;

if( $item ~~ @array )
    {
    say "The array contains $item"
    }

if( $item ~~ %hash )
    {
    say "The hash contains $item"
    }

对于早期版本的 Perl,您必须做更多的工作。如果您要对任意字符串值多次进行此查询,最快的方法可能是反转原始数组并维护一个哈希,其键是第一个数组的值:

@blues = qw/azure cerulean teal turquoise lapis-lazuli/;
%is_blue = ();
for (@blues) { $is_blue{$_} = 1 }

现在您可以检查是否 $is_blue{$some_color}. 首先将布鲁斯全部保存在哈希中可能是一个好主意。

如果这些值都是小整数,则可以使用简单的索引数组。这种数组将占用更少的空间:

@primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31);
@is_tiny_prime = ();
for (@primes) { $is_tiny_prime[$_] = 1 }
# or simply  @istiny_prime[@primes] = (1) x @primes;

现在你检查 $is_tiny_prime[$some_number].

如果有问题的值是整数而不是字符串,则可以通过使用位字符串来节省大量空间:

@articles = ( 1..10, 150..2000, 2017 );
undef $read;
for (@articles) { vec($read,$_,1) = 1 }

现在检查 vec($read,$n,1) 对于某些 $n 是否为真。

这些方法保证了快速的单独测试,但需要重新组织原始列表或数组。只有当您必须针对同一个数组测试多个值时,它们才会得到回报。

如果您只测试一次,标准模块 List::Util 为此首先导出该函数。一旦找到元素,它就会停止工作。为了速度,它是用 C 语言编写的,它的 Perl 等效程序如下所示:

sub first (&@) {
    my $code = shift;
    foreach (@_) {
        return $_ if &{$code}();
    }
    undef;
}

如果速度无关紧要,常见的习惯用法在标量上下文中使用 grep(它返回通过其条件的项目数)来遍历整个列表。不过,这确实有利于告诉您它找到了多少匹配项。

my $is_there = grep $_ eq $whatever, @array;

如果您想实际提取匹配元素,只需在列表上下文中使用 grep 即可。

my @matches = grep $_ eq $whatever, @array;
于 2009-04-06T12:50:51.117 回答
16

使用List::Util中的第一个函数,它是 Perl 的标准......

use List::Util qw/first/;

my @a = qw(foo bar baz);
if ( first { $_ eq 'bar' } @a ) { say "Found bar!" }

注意。first返回它找到的第一个元素,因此不必遍历完整列表(这是grep将要做的)。

于 2009-04-06T09:41:21.510 回答
8

一种可能的方法是使用 List::MoreUtils 'any' 函数。

use List::MoreUtils qw/any/;

my @array = qw(foo bar baz);

print "Exist\n" if any {($_ eq "foo")} @array;

更新:根据 zoul 的评论更正。

于 2009-04-06T07:34:54.060 回答
5

有趣的解决方案,特别是对于重复搜索:

my %hash;
map { $hash{$_}++ } @a;
print $hash{$val};
于 2009-04-06T07:37:29.873 回答
2
$ perl -e '@a = qw(foo bar baz);$val="bar";
if (grep{$_ eq $val} @a) {
  print "found"
} else {
  print "not found"
}'

成立

$val='baq';

未找到

于 2009-04-06T08:26:00.927 回答
1

any如果first您不喜欢不必要的依赖,请自行实施

sub first (&@) {
  my $code = shift;
  $code->() and return $_ foreach @_;
  undef
}

sub any (&@) {
  my $code = shift;
  $code->() and return 1 foreach @_;
  undef
}
于 2009-04-07T19:11:56.933 回答