Почему круглые скобки необязательны только после вложенной декларации? - PullRequest
29 голосов
/ 13 мая 2011

(Предположим, use strict; use warnings; на протяжении всего этого вопроса.)

Я изучаю использование sub.

sub bb { print @_; }
bb 'a';

Это работает как ожидалось. Скобки не обязательны, как и во многих других функциях, например print, open и т. Д.

Однако это приводит к ошибке компиляции:

bb 'a';
sub bb { print @_; }

String found where operator expected at t13.pl line 4, near "bb 'a'"
        (Do you need to predeclare bb?)
syntax error at t13.pl line 4, near "bb 'a'"
Execution of t13.pl aborted due to compilation errors.

Но это не так:

bb('a');
sub bb { print @_; }

Аналогично, подпрограмма без аргументов, например:

special_print;
my special_print { print $some_stuff }

Вызовет эту ошибку:

Bareword "special_print" not allowed while "strict subs" in use at t13.pl line 6.
Execution of t13.pl aborted due to compilation errors.

Способы устранения этой конкретной ошибки:

  • Поставьте & перед именем, например, &special_print
  • Поставьте пустые скобки после имени, например, special_print()
  • Predeclare special_print с sub special_print в верхней части скрипта.
  • Позвоните special_print после вложенной декларации.

У меня вопрос, почему это специальное лечение? Если я могу использовать подпрограмму глобально в скрипте, почему я не могу использовать ее так, как мне хочется? Есть ли логика для реализации sub таким образом?

ETA: я знаю, как я могу это исправить. Я хочу знать логику этого.

Ответы [ 4 ]

33 голосов
/ 13 мая 2011

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

В Perl синтаксис sub NAME для объявления подпрограммы эквивалентен следующему:

sub name {...}   ===   BEGIN {*name = sub {...}}

Это означает, что синтаксис sub NAME имеет эффект времени компиляции. Когда Perl анализирует исходный код, он работает с текущим набором объявлений. По умолчанию набор встроенных функций. Поскольку Perl уже знает об этом, он позволяет опустить круглые скобки.

Как только компилятор нажимает на блок BEGIN, он компилирует внутреннюю часть блока, используя текущий набор правил, а затем немедленно выполняет блок. Если что-либо в этом блоке изменит набор правил (например, добавление подпрограммы в текущее пространство имен), эти новые правила будут действовать в течение оставшейся части разбора.

Без предварительно объявленного правила идентификатор будет интерпретироваться следующим образом:

bareword       ===   'bareword'   # a string
bareword LIST  ===   syntax error, missing ','
bareword()     ===   &bareword()  # runtime execution of &bareword
&bareword      ===   &bareword    # same
&bareword()    ===   &bareword()  # same

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

Когда предварительно объявлено любое из следующего:

sub bareword;
use subs 'bareword';
sub bareword {...}
BEGIN {*bareword = sub {...}}

Тогда идентификатор будет интерпретироваться следующим образом:

bareword      ===   &bareword()     # compile time binding to &bareword
bareword LIST ===   &bareword(LIST) # same
bareword()    ===   &bareword()     # same
&bareword     ===   &bareword       # same
&bareword()   ===   &bareword()     # same

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

Что касается того, что стоит за всем этим, у Perl много наследства. Одной из целей разработки Perl была полная обратная совместимость. Скрипт, который работает в Perl 1, все еще работает в Perl 5. Из-за этого невозможно изменить правила, связанные с анализом голых слов.

Тем не менее, вам будет сложно найти язык, который будет более гибким в способах вызова подпрограмм. Это позволяет вам найти метод, который лучше всего подходит для вас. В моем собственном коде, если мне нужно вызвать подпрограмму до того, как она была объявлена, я обычно использую name(...), но если у этой подпрограммы есть прототип, я назову ее как &name(...) (и вы получите предупреждение «Подпрограмма» Вызов слишком рано, чтобы проверить прототип ", если вы не называете это так).

17 голосов
/ 13 мая 2011

Лучший ответ, который я могу придумать, это то, как написан Perl. Это не удовлетворительный ответ, но, в конце концов, это правда. Perl 6 (если он когда-либо выйдет) не будет иметь этого ограничения.

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

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

b $a, $c;

Perl не может знать, является ли b строкой и является просто голым словом (что было разрешено в Perl 4) или если b является функцией. Если b является функцией, она должна быть сохранена в таблице символов, так как остальная часть программы анализируется. Если b не является подпрограммой, не следует помещать ее в таблицу символов.

Когда компилятор Perl видит это:

b($a, $c);

Он не знает, что делает функция b , но, по крайней мере, знает, что это функция, и может сохранить ее в таблице символов в ожидании определения позже.

Когда вы предварительно объявляете свою функцию, Perl может видеть это:

sub b;   #Or use subs qw(b); will also work.

b $a, $c;

и знайте, что b - это функция. Возможно, он не знает, что делает функция, но теперь есть запись таблицы символов для b в качестве функции.

Одной из причин для Perl 6 является удаление большей части багажа, оставшегося от старых версий Perl, и удаление странных вещей, подобных этой.

Кстати, никогда не используйте прототипы Perl, чтобы обойти это ограничение. Используйте use subs или предварительно объявите пустую подпрограмму. Не используйте прототипы.

6 голосов
/ 13 мая 2011

Скобки необязательны, только если подпрограмма была предварительно объявлена. Это задокументировано в perlsub .

Perl должен знать во время компиляции, является ли голое слово именем подпрограммы или строковым литералом. Если вы используете круглые скобки, Perl будет догадываться, что это имя подпрограммы. В противном случае вам необходимо предоставить эту информацию заранее (например, используя subs).

0 голосов
/ 13 мая 2011

Причина в том, что Ларри Уолл - лингвист, а не специалист по информатике.

Специалист по информатике : Грамматика языка должна быть максимально простой и понятной.

  • Предотвращает сложность в компиляторе
  • Устраняет источники неоднозначности

Ларри Уолл : Люди работают иначе, чем компиляторы.Язык должен служить программисту, а не компилятору.См. Также план Ларри Уолла о трех достоинствах программиста .

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