Почему Perl позволяет вызывать coderefs для незагруженных структур данных?

#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:

В первом примере вы пытаетесь вызвать метод ссылки на массив по имени; ссылка на массив не содержит никаких методов, так что это даже не имеет смысла.

Во втором примере вы используете аналогичный синтаксис, но в этом случае -> это просто синтаксический сахар для вызова правого параметра с левым параметром в качестве первого аргумента; это не вызов метода экземпляра, это просто вызов подпрограммы с параметром, указанным непонятным образом.