2009-08-21 3 views
2

Как бы написать функцию, которая принимает что-то вроде функции карты?Как передать функцию на Perl?

Пример:

$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" }); 

это не будет работать, и говорю, что функ является неопределенной ссылкой.

Таким образом, мой вопрос заключается в том, как написать функцию, которая принимает параметры, такие как функция перлов-карт?

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

ответ

17

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

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

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

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. Это действительно единственный случай, когда нужно использовать что-то подобное.

+0

+1. Мне нравится идея локализации $ _ с оператором «for» - позволяет 1-arg funcrefs быть красивым и лаконичным. (И я полагаю, вы могли бы «локально» ($ a, $ b); »для 2-arg funcrefs a la sort().) –

1
&something({ print $_[0], "hi\n" }); 
#   ^^^ this isn't a reference 

&something(sub { print $_[0], "hi\n" }); # works just fine 
+0

Почему вы должны '&' на фронте? –

+0

Предположительно из-за того, что у OP был один, и ответ копировал стиль OP. Несмотря на то, что префикс, вероятно, не должен быть там, как объяснил Синан Унюр. –

6

Прежде всего, не используйте & при звонке sub s. От perldoc perlsub:

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

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

sub something(&@); 

# later 

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

} 

См Prototypes.

лично я бы просто передать явную subref:

something(sub { }); 
Смежные вопросы