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
。 这使得它更像是if
和unless
。
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'); }