#perl #methods #coderef
#perl #методы #coderef
Вопрос:
При выполнении инструкции $obj->method();
, perldiag
говорится, что Perl должен знать, к какому пакету принадлежит метод. Вот почему он должен быть благословлен:
Не удается вызвать метод «%s» для необработанной ссылки
(F) Вызов метода должен знать, в каком пакете он должен выполняться. Обычно это выясняется из предоставленной вами ссылки на объект, но в данном случае вы не указали ссылку на объект. Ссылка не является ссылкой на объект, пока она не была благословлена. Смотрите
perlobj
.
Из-за этого невозможно выполнить следующее:
my $data = [
[ 1, 2, 3 ],
[ 4, 5, 6 ],
];
$data->process( @params ); # Can't call method "process" on unblessed reference
Тогда почему это работает с coderef?:
my $process = amp;process; # Same method as before
$data->$process( @params ); # Works fine now
Комментарии:
1. кроме того, lhs
->
даже не обязательно должно быть ссылкой, когда rhs является code:my $add = sub {$_[0] $_[1]}; say 1->$add(2);
. Литеральные ссылки тоже хороши:[[1,2,3],[4,5,6]]->$process(@params)
Ответ №1:
$variable->method(@args)
просто вызывает
method($variable, @args)
Но в каком пакете Perl должен найти method
подпрограмму? Если $variable
это благословенная ссылка, Perl будет искать подпрограмму в пакете, возвращаемом ref $variable
. Если $variable
это строка и имя пакета, Perl будет искать подпрограмму в этом имени пакета.
Во втором примере, когда вы объявляете
$process = amp;process
вы предоставили Perl ссылку на код, который хотите вызвать, поэтому Perl знает, что нужно принять вызов
$variable->$process(@args)
и вызывать
$process->($variable, @args)
или
amp;process($variable, @args)
Только тогда, когда $variable
это незагруженная ссылка и имя метода не может быть преобразовано в ссылку на код, Perl не может понять, что делать, и останавливается.
Комментарии:
1. 1 : Имеет смысл. Но есть ли все еще двусмысленность в одном скрипте, который не имеет нескольких пакетов?
2. У каждого скрипта есть несколько пакетов (попробуйте
perl -E 'say for grep/::$/,keys %main::
). Но более важный момент заключается в том, что каждый вызов подпрограммы всегда выполняется в отношении пакета (за исключением, возможно, вызовов анонимных вспомогательных модулей), даже если синтаксис Perl часто скрывает некоторые детали. В компиляторе нет механизма для получения неквалифицированного имени подпрограммы и поиска во всех доступных пространствах имен соответствующего имени подпрограммы.
Ответ №2:
Когда perl видит $x->$y
, он компилирует это во что-то вроде:
if (reftype $y eq 'CODE') {
$y->($x)
}
else {
if (my $code_ref = eval {$x->can($y)}) {
$code_ref->($x)
}
else {
die "error"
}
}
Если $y
это простое слово, первая проверка всегда ложна, а затем else
блок выполняет обычный вызов метода.
Ответ №3:
В первом примере вы пытаетесь вызвать метод ссылки на массив по имени; ссылка на массив не содержит никаких методов, так что это даже не имеет смысла.
Во втором примере вы используете аналогичный синтаксис, но в этом случае ->
это просто синтаксический сахар для вызова правого параметра с левым параметром в качестве первого аргумента; это не вызов метода экземпляра, это просто вызов подпрограммы с параметром, указанным непонятным образом.