Как я могу передать функцию в суб Perl? - PullRequest
2 голосов
/ 21 августа 2009

Как написать функцию, которая принимает что-то похожее на функцию карты?

Пример:

$func = sub { print $_[0], "hi\n" };
&something($f);
sub something
{
    my $func = shift;
    for ($i = 0; $i < 5; $i++)
    {    $func->($i);    }
}

отлично работает.

но тогда, если бы я сделал

&something( { print $_[0], "hi\n" } );

это не сработает и говорит, что func - неопределенная ссылка.

Итак, мой вопрос: как написать функцию, которая принимает параметры, такие как функция сопоставления perls?

map { s/a/b/g } @somelist;

Ответы [ 3 ]

17 голосов
/ 21 августа 2009

Функция map имеет очень магический синтаксис, и вы, вероятно, не захотите копировать ее, если у вас нет действительно веской причины; просто используйте обычный анонимный саб, как это:

something(sub { print $_[0], "hi\n" });

Если вы действительно хотите это сделать, вам нужно использовать прототип :

sub my_map (&@) {
    my ($func, @values) = @_;
    my @ret;
    for (@values) {
        push @ret, $func->($_);
    }
    return @ret;
}

my @values = my_map { $_ + 1 } qw(1 2 3 4);
print "@values";  # 2 3 4 5

(Обратите внимание, что $_ динамически ограничен, поэтому любое значение, которое он имеет в вызывающем, сохраняется в функции.)

List::Util и List::MoreUtils много делают для создания функций, которые выглядят встроенными и действуют как варианты map / grep. Это действительно единственный случай, когда нужно использовать что-то подобное.

6 голосов
/ 21 августа 2009

Во-первых, не используйте & при вызове sub с. От perldoc perlsub:

Подпрограммы могут вызываться рекурсивно. Если подпрограмма вызывается с использованием формы &, список аргументов является необязательным, и, если он опущен, для подпрограммы не устанавливается массив @_: вместо этого массив @_ во время вызова является видимым для подпрограммы , Это механизм эффективности, которого новые пользователи могут пожелать избежать.

Если вы хотите, чтобы можно было передать простой блок "sub something", вам нужно использовать прототип, как в:

sub something(&@);

# later

sub something(&@) {
    my ($coderef, @args) = @_;

}

См. Прототипы .

Лично я бы просто передал явный подреф:

something( sub { } );
1 голос
/ 21 августа 2009
&something( { print $_[0], "hi\n" } );
#           ^^^ this isn't a reference

&something( sub { print $_[0], "hi\n" } ); # works just fine
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...