如何验证 Perl 中的数组(列表)中是不是存在值?
Posted
技术标签:
【中文标题】如何验证 Perl 中的数组(列表)中是不是存在值?【英文标题】:How can I verify that a value is present in an array (list) in Perl?如何验证 Perl 中的数组(列表)中是否存在值? 【发布时间】:2010-10-17 18:07:46 【问题描述】:我有一个可能值的列表:
@a = qw(foo bar baz);
如何以简洁的方式检查 $val
值在 @a
中是否存在?
一个明显的实现是遍历列表,但我确信TMTOWTDI。
感谢所有回答的人!我想强调的三个答案是:
公认的答案 - 最“内置”且向后兼容的方式。
RET's answer 是最干净的,但只适用于 Perl 5.10 及更高版本。
draegtun's answer(可能)快一点,但需要使用额外的模块。如果可以避免它们,我不喜欢添加依赖项,并且在这种情况下不需要性能差异,但是如果您有一个 1,000,000 个元素的列表,您可能想尝试一下这个答案。
【问题讨论】:
我不确定是否看到 List::Util 的依赖问题。它是 Perl 的标准,如果你将它与 qw/first/ 一起使用(就像 Draegtun 所做的那样),你只导入一个子例程。 这本身不是问题,更多的是个人喜好。 List::Util 答案没有依赖性问题。如果是我,那将是公认的答案。不愿意使用核心模块让我觉得这是一种根植于迷信的偏好。在这种情况下,grep 几乎一样好。 【参考方案1】:如果你有 perl 5.10,请使用 smart-match operator ~~
print "Exist\n" if $var ~~ @array;
这几乎是魔法。
【讨论】:
注意。从 5.10.1 开始,语义发生了一些变化,它必须是:if $var ~~ @array
。为了帮助我将~~
视为in
。参考:perldoc.perl.org/perldelta.html#Smart-match-changes
谢谢 - 已相应地交换了订单。
我的 perldoc url 不再有效。这是固定的:search.cpan.org/~dapm/perl-5.10.1/pod/…【参考方案2】:
Perl 的 grep() 函数中的 bulit 就是为此而设计的。
@matches = grep( /^MyItem$/, @someArray );
或者你可以在匹配器中插入任何表达式
@matches = grep( $_ == $val, @a );
【讨论】:
【参考方案3】:perlfaq4 对"How can I tell whether a certain element is contained in a list or array?" 的回答中对此进行了回答。
要搜索 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;
【讨论】:
【参考方案4】:使用来自List::Util 的 first 函数,它是 Perl 的标准......
use List::Util qw/first/;
my @a = qw(foo bar baz);
if ( first $_ eq 'bar' @a ) say "Found bar!"
注意。 first 返回它找到的第一个元素,因此不必遍历整个列表(这是 grep 将做的)。
【讨论】:
每个愚蠢的小子,如果使用导入的子,我更喜欢 List::MoreUtil::any() 因为这个概念(“如果 LIST 中的任何项目符合标准,则返回真值”)在语义上比 first() 更好地匹配问题(“返回 BLOCK 结果为真值的第一个元素。”) List::Util::first() 具有作为核心模块的优势(即无处不在)。如果我正在寻找 CPAN 选项,那么我会认真考虑 Perl6::Junction::any... if (any(@a) eq 'baz' ) 【参考方案5】:一种可能的方法是使用 List::MoreUtils 'any' 函数。
use List::MoreUtils qw/any/;
my @array = qw(foo bar baz);
print "Exist\n" if any ($_ eq "foo") @array;
更新:根据zoul的评论更正。
【讨论】:
Defined("foo") 总是正确的,你的意思是 $_ eq 'foo'? 我一直在摘掉我给你的 -1 并且它一直把它重新戴上......希望它在这(第三)次保持关闭!【参考方案6】:有趣的解决方案,尤其是重复搜索:
my %hash;
map $hash$_++ @a;
print $hash$val;
【讨论】:
另外,虽然这很有效并且很常见,但有些人(我猜包括我自己)会抱怨在 void 上下文中使用 map。为什么不用 $hash$_++ 代替 @a?【参考方案7】:$ perl -e '@a = qw(foo bar baz);$val="bar";
if (grep$_ eq $val @a)
print "found"
else
print "not found"
'
找到
$val='baq';
找不到
【讨论】:
【参考方案8】:如果您不喜欢不必要的依赖,请自行实现any
或first
sub first (&@)
my $code = shift;
$code->() and return $_ foreach @_;
undef
sub any (&@)
my $code = shift;
$code->() and return 1 foreach @_;
undef
【讨论】:
以上是关于如何验证 Perl 中的数组(列表)中是不是存在值?的主要内容,如果未能解决你的问题,请参考以下文章