Как бы я сделал эквивалент Enumerator.detect Prototype в Perl с наименьшим количеством кода? - PullRequest
6 голосов
/ 19 января 2010

В последнее время я много думал о функциональном программировании. Perl предлагает довольно много инструментов для этого, но есть кое-что, что я пока не смог найти.

Prototype имеет функцию обнаружения для перечислителей, описания просто такие:

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

В этом случае перечислителем является любой список, а итератор - ссылка на функцию, которая поочередно применяется к каждому элементу списка.

Я ищу что-то подобное, чтобы применять его в ситуациях, когда важна производительность, то есть остановка при встрече с матчем экономит время, игнорируя остальную часть списка.

Я также ищу решение, которое бы не включало загрузку какого-либо дополнительного модуля, поэтому, если возможно, это должно быть сделано только со встроенными модулями. И, если возможно, он должен быть таким же кратким, как это, например:

my @result = map function @array;

Ответы [ 3 ]

15 голосов
/ 19 января 2010

Вы говорите, что вам не нужен модуль, но это именно то, что делает функция first в List :: Util .Это основной модуль, поэтому он должен быть доступен везде.

use List::Util qw(first);
my $first = first { some condition } @array;

Если вы настаиваете на том, чтобы не использовать модуль, вы можете скопировать реализацию из List :: Util.Если бы кто-то знал более быстрый способ сделать это, он был бы там.(Обратите внимание, что List :: Util включает реализацию XS, так что это, вероятно, быстрее, чем любой подход с чистым Perl. У него также есть версия с чистым Perl first в List :: Util :: PP.)

Обратите внимание, что проверяемое значение передается подпрограмме в $_ и , а не в качестве параметра.Это удобно, когда вы используете форму 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; 
5 голосов
/ 19 января 2010

Не проверено, поскольку у меня нет 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;

Обратите внимание, что это не делает различий между отсутствием совпадений в списке и тем, что один из элементов в списке является неопределенным значением и имеет это совпадение.

4 голосов
/ 19 января 2010

Так как его здесь нет, определение первой функции 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). что намного быстрее

...