Perl:if(列表中的元素)

我正在寻找一个列表中的元素的存在。

在Python中有一个关键字,我会做这样的事情:

 if element in list: doTask 

Perl中是否有相同的东西,而不必手动迭代整个列表?

更新:

smartmatchfunction家族现在是实验性的

智能匹配,在v5.10.0中添加,并在v5.10.1中进行了重大修改,一直是一个常见的投诉点。 虽然有很多方法是有用的,但对于Perl的用户和实现者来说,它也被certificate是有问题和困惑的。 关于如何最好地解决这个问题已经有了一些build议。 很明显,智能匹配几乎肯定要么改变,要么将在未来消失。 不build议依靠其目前的行为。

当parsing器看到~~,给定或什么时候,警告将被发出。




如果你需要Perl v5.10,那么你可以使用下面的任何一个例子。

  • 智能匹配~~运算符。

     if( $element ~~ @list ){ ... } if( $element ~~ [ 1, 2, 3 ] ){ ... } 
  • 你也可以使用given / when结构。 在内部使用智能匹配function。

     given( $element ){ when( @list ){ ... } } 
  • 您也可以使用for循环作为“专题化器”(意思是设置$_ )。

     for( @elements ){ when( @list ){ ... } } 

在Perl 5.12中会出现的一件事是能够使用post-fix版本的when 。 这使得它更像是ifunless

 given( $element ){ ... when @list; } 

如果你必须能够在老版本的Perl上运行,那么仍然有几个select。

  • 你可能会认为你可以放弃使用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 } ){ ... } 
  • …但在if语句之前放置for循环可能会更清楚。

     my $match = 0; for( @list ){ if( $_ eq $element ){ $match = 1; last; } } if( $match ){ ... } 
  • 如果你只是匹配string,你也可以使用散列。 这可以加快你的程序,如果@list很大,你会匹配%hash几次。 特别是如果@array不改变,因为那么你只需要加载一次%hash

     my %hash = map { $_, 1 } @array; if( $hash{ $element } ){ ... } 
  • 你也可以做你自己的子程序。 这是使用原型有用的情况之一。

     sub in(&@){ local $_; my $code = shift; for( @_ ){ # sets $_ if( $code->() ){ return 1; } } return 0; } if( in { $element eq $_ } @list ){ ... } 
 if( $element ~~ @list ){ do_task } 

~~是“智能匹配运算符”,不仅仅是列表成员检测。

列表::的Util ::第一

 $foo = first { ($_ && $_ eq "value" } @list; # first defined value in @list 

或者对于手动滚动types:

 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) { ... 

如果您打算多次执行此操作,则可以在空间上查找时间:

 #!/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的内容是简单的标量。

TIMTOWTDI

 sub is (&@) { my $test = shift; $test->() and return 1 for @_; 0 } sub in (@) {@_} if( is {$_ eq "a"} in qw(dcba) ) { print "Welcome in perl!\n"; } 

列表:: MoreUtils

在perl> = 5.10的情况下,智能匹配运算符肯定是最简单的方法,就像其他人已经说过的那样。

在旧版本的Perl上,我会build议List :: MoreUtils :: any 。

List::MoreUtils不是一个核心模块(有人说它应该是),但它是非常stream行的,它包含在主要的perl发行版中。

它具有以下优点:

  • 它会返回true / false(如Python中的那样)而不是元素的值,如List::Util::first (这使得很难testing,如上所述);
  • 不像grep ,它停在第一个通过testing的元素上(perl的智能匹配操作符短路 )。
  • 它适用于任何Perl版本(至less> = 5.00503)。

这是一个可以与任何search(标量)值一起工作的例子,包括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" } 

PS

(在生产代码中,最好缩小no warnings 'uninitialized'的范围)。

grep在这里很有帮助

 if (grep { $_ eq $element } @list) { .... } 

可能Perl6::Junction是最明确的方法。 没有XS的依赖,没有混乱,没有新的Perl版本的要求。

 use Perl6::Junction qw/ any /; if (any(@grant) eq 'su') { ... } 

这篇博文讨论了这个问题的最佳答案。

作为一个简短的总结,如果你可以安装CPAN模块,那么最好的解决scheme是:

 if any(@ingredients) eq 'flour'; 

要么

 if @ingredients->contains('flour'); 

不过,更常见的成语是:

 if @any { $_ eq 'flour' } @ingredients 

我发现不太清楚。

但请不要使用第一个()函数! 它根本不expression你的代码的意图。 不要使用“智能匹配”操作符:已损坏。 不要使用grep(),也不要使用哈希方法:遍历整个列表。 尽pipe任何()都会在find您的价值时立即停止。

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

PS:我正在回答将来会有同样问题的人。

如果你做一些自动加载黑客,你可以在Perl中完成类似的语法。

创build一个小包来处理自动加载:

 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'); 

function做到这一点:

 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'); }