Perl:如果(列表中的元素)
Posted
技术标签:
【中文标题】Perl:如果(列表中的元素)【英文标题】:Perl: if ( element in list ) 【发布时间】:2011-01-23 22:04:43 【问题描述】:我正在寻找列表中是否存在某个元素。
在 Python 中有一个 in
关键字,我会这样做:
if element in list:
doTask
在 Perl 中有没有等价的东西而不必手动遍历整个列表?
【问题讨论】:
【参考方案1】:更新:
The smartmatch family of features are now experimental
在 v5.10.0 中添加并在 v5.10.1 中进行了重大修订的智能匹配一直是投诉的常规点。尽管它有很多有用的方式,但它也被证明对于 Perl 的用户和实现者来说是有问题的和令人困惑的。关于如何最好地解决这个问题,已经提出了许多建议。很明显,smartmatch 几乎肯定会在未来改变或消失。不建议依赖其当前行为。
现在,当解析器看到 ~~、given 或 when 时会发出警告。
如果您不需要 Perl v5.10,那么您可以使用以下任何示例。
smart match ~~
运算符。
if( $element ~~ @list ) ...
if( $element ~~ [ 1, 2, 3 ] ) ...
您也可以使用given
/when
构造。它在内部使用智能匹配功能。
given( $element )
when( @list ) ...
您还可以将 for
循环用作“主题化器”(意味着它设置了 $_
)。
for( @elements )
when( @list ) ...
Perl 5.12 中将出现的一件事是能够使用when
的后修复版本。这使它更像if
和unless
。
given( $element )
... when @list;
如果您必须能够在旧版本的 Perl 上运行,仍然有多种选择。
您可能认为使用 List::Util::first 可以侥幸成功,但有一些边缘条件会导致问题。
在这个例子中,很明显我们想要成功匹配0
。不幸的是,这段代码每次都会打印failure
。
use List::Util qw'first';
my $element = 0;
if( first $element eq $_ 0..9 )
print "success\n";
else
print "failure\n";
您可以检查 first
的返回值是否已定义,但如果我们真的希望与 undef
的匹配成功,那将失败。
不过,您可以安全地使用grep
。
if( grep $element eq $_ 0..9 ) ...
这是安全的,因为grep
在标量上下文中被调用。数组在标量上下文中调用时返回元素的数量。因此,即使我们尝试匹配 undef
,这将继续有效。
您可以使用封闭的for
循环。只需确保您调用last
,以在成功匹配时退出循环。否则你可能会多次运行你的代码。
for( @array )
if( $element eq $_ )
...
last;
您可以将for
循环放在if
语句的条件中...
if(
do
my $match = 0;
for( @list )
if( $element eq $_ )
$match = 1;
last;
$match; # the return value of the do block
)
...
...但将for
循环放在if
语句之前可能更清楚。
my $match = 0;
for( @list )
if( $_ eq $element )
$match = 1;
last;
if( $match ) ...
如果您只匹配字符串,您也可以使用哈希。如果@list
很大并且,您将与%hash
多次匹配,这可以加快您的程序。特别是如果@array
没有改变,因为那样你只需要加载一次%hash
。
my %hash = map $_, 1 @array;
if( $hash $element ) ...
您也可以制作自己的子程序。这是使用prototypes 很有用的情况之一。
sub in(&@)
local $_;
my $code = shift;
for( @_ ) # sets $_
if( $code->() )
return 1;
return 0;
if( in $element eq $_ @list ) ...
【讨论】:
@xxxxxxx 你这么说,但我投票最高的答案通常也是最长的。 @BradGilbert 哇!我喜欢运营商~~
!它是如此波浪和乐于助人! :)
请注意,“given”、“when”和“smart match operator”现在是 marked as "experimental" since perl 5.18 并将生成警告。答案的smart match ~~ 链接也不再有任何#Smart-matching-in-detail
片段。
下一次编辑应该将智能匹配链接切换到perldoc.perl.org/perlop.html#Smartmatch-Operator【参考方案2】:
if( $element ~~ @list )
do_task
~~
是“智能匹配算子”,它不仅仅做列表成员检测。
【讨论】:
请注意,“智能匹配运算符”现在是 marked as "experimental" since perl 5.18 并将生成警告。答案的smart match ~~ 链接也不再有任何#Smart-matching-in-detail
片段。【参考方案3】:
如果您打算多次执行此操作,您可以用空间换取查找时间:
#!/usr/bin/perl
use strict; use warnings;
my @array = qw( one ten twenty one );
my %lookup = map $_ => undef @array;
for my $element ( qw( one two three ) )
if ( exists $lookup $element )
print "$element\n";
假设元素在@array
中出现的次数并不重要,@array
的内容是简单的标量。
【讨论】:
好技术,绝对值得一提。 很好的技术,提到它只有在进行多次查找时才会得到回报。 +1【参考方案4】:List::Util::first
$foo = first ($_ && $_ eq "value" @list; # first defined value in @list
或者对于手卷类型:
my $is_in_list = 0;
foreach my $elem (@list)
if ($elem && $elem eq $value_to_find)
$is_in_list = 1;
last;
if ($is_in_list)
...
稍微不同的版本在很长的列表上可能会更快一些:
my $is_in_list = 0;
for (my $i = 0; i < scalar(@list); ++$i)
if ($list[i] && $list[i] eq $value_to_find)
$is_in_list = 1;
last;
if ($is_in_list)
...
【讨论】:
这简直是半途而废。 -1【参考方案5】:grep
在这里很有帮助
if (grep $_ eq $element @list)
....
【讨论】:
List::Util::first
可能是一种更有效的方法。
或者效率更高,如果@list 很大,因为 List::Util::first 不会继续超过第一个匹配,但 grep 会。
我用一个大列表对此进行了测试,两者都非常快。当速度差异明显时,我的机器已经烧毁了 6 GB 的 RAM。如果您的列表是 qw(foo bar baz),则可能无关紧要。
除了匹配元素未接近末尾的大型数据集外,这是获得答案的好方法。
@jrockway 但是List::Util::first
不是grep
的直接替代品。由于 grep 在这里是在标量上下文中调用的,因此您将获得匹配元素的数量。如果您改用List::Util::first
,那么您可能会检索到比较为假的@list
元素...【参考方案6】:
列表::MoreUtils
正如许多其他人已经说过的,在 perl >= 5.10 上,智能匹配运算符无疑是最简单的方法。
在旧版本的 perl 上,我建议使用List::MoreUtils::any。
List::MoreUtils
不是核心模块(有人说应该是),但它非常流行,并且包含在主要的 perl 发行版中。
具有以下优点:
它返回真/假(就像 Python 的in
所做的那样)而不是元素的值,就像 List::Util::first
所做的那样(这使得测试变得困难,如上所述);
与grep
不同,它在第一个通过测试的元素处停止(perl 的智能匹配运算符短路 也是如此);
它适用于任何 perl 版本(至少 >= 5.00503)。
这是一个适用于任何搜索(标量)值的示例,包括undef
:
use List::MoreUtils qw(any);
my $value = 'test'; # or any other scalar
my @array = (1, 2, undef, 'test', 5, 6);
no warnings 'uninitialized';
if ( any $_ eq $value @array )
print "$value present\n"
附言
(在生产代码中最好缩小no warnings 'uninitialized'
的范围)。
【讨论】:
请注意,“智能匹配运算符”现在是 marked as "experimental" since perl 5.18 并将生成警告。答案的smart match ~~ 链接也不再有任何#Smart-matching-in-detail
片段。
List::Util
似乎也有any
。【参考方案7】:
TIMTOWTDI
sub is (&@)
my $test = shift;
$test->() and return 1 for @_;
0
sub in (@) @_
if( is $_ eq "a" in qw(d c b a) )
print "Welcome in perl!\n";
【讨论】:
我不喜欢它,我讨厌 Perl。【参考方案8】:可能Perl6::Junction
是最清晰的方法。没有 XS 依赖,没有混乱,也不需要新的 perl 版本。
use Perl6::Junction qw/ any /;
if (any(@grant) eq 'su')
...
【讨论】:
【参考方案9】:This blog post 讨论了这个问题的最佳答案。
作为一个简短的总结,如果您可以安装 CPAN 模块,那么最好的解决方案是:
if any(@ingredients) eq 'flour';
或
if @ingredients->contains('flour');
不过,更常用的成语是:
if @any $_ eq 'flour' @ingredients
我觉得不太清楚。
但请不要使用 first() 函数!它根本不表达您的代码的意图。不要使用“智能匹配”运算符:它已损坏。并且不要使用 grep() 也不要使用带有哈希的解决方案:它们会遍历整个列表。而 any() 将在找到您的值后立即停止。
查看博文了解更多详情。
PS:我正在为将来有同样问题的人回答。
【讨论】:
我认为通常的成语是if any $_ eq 'flour' @ingredients
,但你必须记住use List::MoreUtils qw/ any /;
。【参考方案10】:
如果你做一些Autoload hacking,你可以在 Perl 中完成类似的语法。
创建一个小包来处理自动加载:
package Autoloader;
use strict;
use warnings;
our $AUTOLOAD;
sub AUTOLOAD
my $self = shift;
my ($method) = (split(/::/, $AUTOLOAD))[-1];
die "Object does not contain method '$method'" if not ref $self->$method eq 'CODE';
goto &$self->$method;
1;
然后您的其他包或主脚本将包含一个子例程,该子例程返回受祝福的对象,该对象在尝试调用其方法时由 Autoload 处理。
sub element
my $elem = shift;
my $sub =
in => sub
return if not $_[0];
# you could also implement this as any of the other suggested grep/first/any solutions already posted.
my %hash; @hash@_ = ();
return (exists $hash$elem) ? 1 : ();
;
bless($sub, 'Autoloader');
这会让你的用法看起来像:
doTask if element('something')->in(@array);
如果你重新组织闭包和它的参数,你可以换一种方式转换语法,让它看起来像这样,有点接近自动框样式:
doTask if search(@array)->contains('something');
功能:
sub search
my @arr = @_;
my $sub =
contains => sub
my $elem = shift or return;
my %hash; @hash@arr = ();
return (exists $hash$elem) ? 1 : ();
;
bless($sub, 'Autoloader');
【讨论】:
以上是关于Perl:如果(列表中的元素)的主要内容,如果未能解决你的问题,请参考以下文章