我如何用最少量的代码在Perl中使用Prototype的Enumerator.detect?

时间:2010-01-18 22:40:56

标签: perl functional-programming

最近我一直在思考函数式编程。 Perl提供了相当多的工具,但是有些东西我还没有找到。

Prototype具有枚举器的函数detect,描述就是这样:

Enumerator.detect(iterator[, context]) -> firstElement | undefined
Finds the first element for which the iterator returns true.

在这种情况下,枚举器是任何列表,而迭代器是对函数的引用,该函数依次应用于列表的每个元素。

我正在寻找类似这样的东西,以便在性能很重要的情况下应用,即在遇到匹配时停止时可以忽略列表的其余部分来节省时间。

我也在寻找一种不涉及加载任何额外模块的解决方案,因此如果可能的话,应该只使用内置模块来完成。如果可能的话,它应该像这样简洁:

my @result = map function @array;

3 个答案:

答案 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)写的,速度要快得多。