最近我一直在思考函数式编程。 Perl提供了相当多的工具,但是有些东西我还没有找到。
Prototype具有枚举器的函数detect,描述就是这样:
Enumerator.detect(iterator[, context]) -> firstElement | undefined
Finds the first element for which the iterator returns true.
在这种情况下,枚举器是任何列表,而迭代器是对函数的引用,该函数依次应用于列表的每个元素。
我正在寻找类似这样的东西,以便在性能很重要的情况下应用,即在遇到匹配时停止时可以忽略列表的其余部分来节省时间。
我也在寻找一种不涉及加载任何额外模块的解决方案,因此如果可能的话,应该只使用内置模块来完成。如果可能的话,它应该像这样简洁:
my @result = map function @array;
答案 0 :(得分:15)
你说你不想要一个模块,但这正是List::Util中的first
函数所做的。这是一个核心模块,所以应该随处可用。
use List::Util qw(first);
my $first = first { some condition } @array;
如果您坚持不使用模块,则可以将实现复制出List :: Util。如果有人知道更快的方法,它会在那里。 (请注意,List :: Util包含一个XS实现,因此可能比任何纯Perl方法更快。它在List :: Util :: PP中也有一个纯Perl版本的first
。)
请注意,正在测试的值将作为参数传递给$_
中的子例程和而不是。当您使用first { some condition} @values
表单时,这是一种方便,但如果您使用常规子例程,则需要记住这些内容。还有一些例子:
use 5.010; # I want to use 'say'; nothing else here is 5.10 specific
use List::Util qw(first);
say first { $_ > 3 } 1 .. 10; # prints 4
sub wanted { $_ > 4 }; # note we're using $_ not $_[0]
say first \&wanted, 1 .. 10; # prints 5
my $want = \&wanted; # Get a subroutine reference
say first \&$want, 1 .. 10; # This is how you pass a reference in a scalar
# someFunc expects a parameter instead of looking at $_
say first { someFunc($_) } 1 .. 10;
答案 1 :(得分:5)
未经测试,因为我在这台机器上没有Perl,但是:
sub first(\&@) {
my $pred = shift;
die "First argument to "first" must be a sub" unless ref $pred eq 'CODE';
for my $val (@_) {
return $val if $pred->($val);
}
return undef;
}
然后将其用作:
my $first = first { sub performing test } @list;
请注意,这并不区分列表中没有匹配项,列表中的某个元素是未定义的值并且具有匹配项。
答案 2 :(得分:4)
只是因为它不在这里,首先将Perl函数定义为其块的$_
本地化:
sub first (&@) {
my $code = shift;
for (@_) {return $_ if $code->()}
undef
}
my @array = 1 .. 10;
say first {$_ > 5} @array; # prints 6
虽然它可以正常工作,但我并不主张使用此版本,因为List::Util
是核心模块(默认安装),first
的实现通常会使用XS版本(用C)写的,速度要快得多。