В последнее время я думал много о функциональном программировании. Perl предлагает довольно много инструментов, чтобы пойти тем путем, однако существует что-то, что я не смог найти все же.
Прототип имеет функцию, обнаруживают для перечислителей, описания просто это:
Enumerator.detect(iterator[, context]) -> firstElement | undefined
Finds the first element for which the iterator returns true.
Перечислитель в этом случае является любым списком, в то время как итератор является ссылкой на функцию, которая применяется в свою очередь на каждом элементе списка.
Я ищу что-то вроде этого для применения в ситуациях, где производительность важна, т.е. когда остановка после обнаружения с соответствием экономит время путем игнорирования остальной части списка.
Я также ищу решение, которое не включило бы загрузку никакого дополнительного модуля, поэтому если возможный это должно быть сделано с builtins только. И, если возможно, это должно быть столь же кратко как это, например:
my @result = map function @array;
Вы говорите, что не хотите модуль, но это именно то, что функция first
в List::Util does::Util. Это основной модуль, поэтому он должен быть доступен везде.
use List::Util qw(first);
my $first = first { some condition } @array;
Если вы настаиваете на том, чтобы не использовать модуль, вы можете скопировать реализацию из List::Util. Если бы кто-то знал более быстрый способ сделать это, он был бы там. (Обратите внимание, что List::Util включает в себя реализацию XS, так что это, вероятно, быстрее, чем любой подход на чистом Perl. Она также имеет чистую перловую версию first
, в List::Util::PP.)
Обратите внимание, что тестируемое значение передается в подпрограмму в $_
и а не в качестве параметра. Это удобно, когда вы сначала используете {некоторый параметр}. @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;
непроверенный, так как у меня нет 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;
Обратите внимание, что это не различает совпадающих в списке, а один из элементов в списке неопределенное значение и имеющее этот матч.
Также с тех пор не здесь, определение функции Perl первого, которое локализует $ _
для его блока:
sub first (&@) {
my $code = shift;
for (@_) {return $_ if $code->()}
undef
}
my @array = 1 .. 10;
say first {$_ > 5} @array; # prints 6
, пока оно будет работать нормально, я не защищаю эту версию, так как Список :: util
- это основной модуль (установленный по умолчанию), а его реализация сначала
обычно использует версию XS (написанную в C), которая намного быстрее.