Как запустить анонимную функцию в Perl? - PullRequest
29 голосов
/ 08 сентября 2011
(sub {
print 1;
})();



sub {
print 1;
}();

Я пробовал разные способы, все не так ...

Ответы [ 6 ]

38 голосов
/ 08 сентября 2011

(sub { ... }) даст вам указатель на функцию, поэтому вы должны вызывать ее по ссылке.

(sub { print "Hello world\n" })->();

Другой простой метод, на который указывает Благовест Буюклиев , заключается в разыменовании указателя функции и вызове этого с использованием операторов { }

&{ sub { print "Hello World" }}();

26 голосов
/ 08 сентября 2011

Да, я не ожидал, что вы, ребята, придумаете столько возможностей. Но вы правы, это perl и TIMTOWTDI : +1 за креативити!

Но, честно говоря, я использую едва ли не другую форму, чем следующая:

Основной синтаксис

my $greet = sub {
    my ( $name ) = @_;
    print "Hello $name\n";
};

# ...

$greet->( 'asker' )

Это довольно просто: sub {} возвращает ссылку на подпрограмму, которую вы можете хранить и передавать как любой другой скаляр. Вы можете позвонить, разыменовав. Существует также второй синтаксис для разыменования: &{ $sub }( 'asker' ), но я лично предпочитаю синтаксис стрелки, потому что я нахожу его более читаемым, и он в значительной степени совпадает с хэрами разыменования $hash->{ $key } и массивами $array->[ $index ]. Более подробную информацию о ссылках можно найти в perldoc perlref .

Я думаю, что другие приведенные примеры немного продвинуты, но почему бы не взглянуть на них:

Goto

sub bar {goto $foo};
bar;

Редко видел и очень боялся в эти дни. Но по крайней мере это goto &function, который считается менее вредным, чем его кривые друзья: goto LABEL или goto EXPRESSION (они устарели с 5.12 и выдают предупреждение). На самом деле есть некоторые обстоятельства, когда вы хотите использовать эту форму, потому что это не обычный вызов функции. Вызывающая функция (bar в данном примере) не будет отображаться в стеке вызовов. И вы не передадите свои параметры, но будет использоваться текущий @_. Посмотрите на это:

use Carp qw( cluck );

my $cluck = sub {
    my ( $message ) = @_;
    cluck $message . "\n";
};


sub invisible {
    @_ = ( 'fake' );
    goto $cluck;
}

invisible( 'real' );

Выход:

fake at bar.pl line 5
    main::__ANON__('fake') called at bar.pl line 14

И в трассировке стека нет ни единого намека на невидимую функцию. Больше информации о goto в perldoc -f goto .

вызовы методов

''->$foo;
# or
undef->$foo;

Если вы вызываете метод для объекта, первым параметром, переданным этому методу, будет инвокант (обычно это экземпляр или имя класса). Я уже говорил, что TIMTOWTCallAFunction ?

# this is just a normal named sub
sub ask {
    my ( $name, $question ) = @_;
    print "$question, $name?\n";
};

my $ask = \&ask; # lets take a reference to that sub 

my $question = "What's up";

'asker'->ask( $question ); # 1: doesn't work

my $meth_name = 'ask';
'asker'->$meth_name( $question ); # 2: doesn't work either

'asker'->$ask( $question ); # 1: this works

В приведенном выше фрагменте есть два вызова, которые не будут работать, потому что perl попытается найти метод с именем ask в пакете asker (на самом деле он будет работать, если этот код находится в указанном пакете). Но третий успешен, потому что вы уже дали Perl правильный метод, и вам не нужно его искать. Как всегда: больше информации в perldoc . Сейчас я не могу найти никакой причины, чтобы оправдать это в рабочем коде.

Заключение

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

12 голосов
/ 08 сентября 2011

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

my $alias = sub {\@_}->(my ($x, $y, $z));

$x = $z = 0;
$y = 1;

print "@$alias"; # '0 1 0'

В противном случае вы обычно сохраняете анонимную подпрограмму в переменной или структуре данных. Следующие стили вызова работают как с переменной, так и с объявлением sub {...}:

dereference arrow:  sub {...}->(args)  or  $code->(args)

dereference sigil:  &{sub {...}}(args) or &$code(args)

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

my $method = sub {...};

$obj->$method           # same as $method->($obj)
$obj->$method(...)      # $method->($obj, ...)

[1, 2, 3]->$method      # $method->([1, 2, 3])
[1, 2, 3]->$method(...) # $method->([1, 2, 3], ...)
5 голосов
/ 08 сентября 2011

Я бесконечно удивлен, находя способы вызова анонимных функций:

$foo = sub {say 1};

sub bar {goto $foo};
bar;

''->$foo; # technically a method, along with the lovely:

undef->$foo;

() = sort $foo 1,1; # if you have only two arguments

и, конечно же, очевидное:

&$foo();
$foo->();
4 голосов
/ 08 сентября 2011

Вам нужен стрелочный оператор:

(sub { print 1;})->();
0 голосов
/ 03 октября 2017

Возможно, вам даже не понадобится анонимная функция, если вы хотите запустить блок кода, и там есть ноль или один вход.Вместо этого вы можете использовать map.

Только для побочного эффекта:

map { print 1 } 1;

Преобразовать данные, позаботиться о присвоении списку:

my ($data) = map { $_ * $_ } 2;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...