Какова роль блока BEGIN в Perl? - PullRequest
34 голосов
/ 22 октября 2010

Я знаю, что блок BEGIN компилируется и выполняется перед основной частью Perl-программы.Если вы не уверены в этом, просто попробуйте выполнить команду perl -cw над этим:

#!/ms/dist/perl5/bin/perl5.8

use strict;
use warnings;

BEGIN {
    print "Hello from the BEGIN block\n";
}

END {
    print "Hello from the END block\n";
}

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

И поэтому я использую блоки BEGIN, чтобы убедиться, что такие вещи, как соединения с БД, установлены и доступны для использования основной программой.Точно так же я использую блоки END, чтобы гарантировать, что все ресурсы закрыты, удалены, прерваны и т. Д. До завершения программы.Блоки END.

Какова предполагаемая роль блока BEGIN в Perl?

Обновление 1: Просто выяснил, почему не работает DBI-соединение.Получив эту маленькую Perl-программу:

use strict;
use warnings;

my $x = 12;

BEGIN {
    $x = 14;
}

print "$x\n";

при выполнении печатает 12.

Обновление 2: Благодаря комментарию Эрика Строма ниже, эта новая версия делает ее более понятной:

use strict;
use warnings;

my $x = 12;
my $y;

BEGIN {
    $x = 14;
    print "x => $x\n";
    $y = 16;
    print "y => $y\n";
}

print "x => $x\n";
print "y => $y\n";

и вывод

x => 14
y => 16
x => 12
y => 16

Еще раз спасибо, Эрик!

Ответы [ 3 ]

31 голосов
/ 22 октября 2010

В то время как блоки BEGIN и END могут использоваться, как вы описываете, типичное использование заключается в внесении изменений, которые влияют на последующую компиляцию.

Например, оператор use Module qw/a b c/; фактически означает:

BEGIN {
   require Module;
   Module->import(qw/a b c/);
}

аналогично, объявление подпрограммы sub name {...} на самом деле:

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

Поскольку эти блоки запускаются во время компиляции, все строки, которые компилируются после выполнения блока, будут использоватьновые определения, которые блоки BEGIN сделали.Это то, как вы можете вызывать подпрограммы без скобок, или как различные модули «изменяют способ работы мира».

END блоки могут использоваться для очистки изменений, которые были сделаны блоками BEGIN, но этоболее распространено использование объектов с методом DESTROY.

Если состояние, которое вы пытаетесь очистить, является соединением DBI, выполнение этого в блоке END хорошо.Я бы не стал создавать соединение в блоке BEGIN, хотя по нескольким причинам.Обычно нет необходимости, чтобы соединение было доступно во время компиляции.Выполнение таких действий, как подключение к базе данных во время компиляции, значительно замедлит работу любого редактора, в котором есть проверка синтаксиса (поскольку он запускает perl -c).

22 голосов
/ 24 октября 2010

Вы пробовали заменить блок BEGIN{} на блок INIT{}?Это стандартный подход для таких вещей, как modperl, в которых используется модель «один раз при компиляции», так как вам нужно заново инициализировать вещи при каждом отдельном запуске, а не только один раз во время компиляции.

Но у меня естьспросить, почему все это в специальном блоке в любом случае.Почему бы вам просто не сделать какую-то функцию prepare_db_connection(), а затем вызывать ее по мере необходимости при запуске программы?

Что-то, что не будет работать в BEGIN{}, также будет иметьта же проблема, если это основной код в файле модуля, который получает use d.Это еще одна возможная причина для использования блока INIT{}.

Я также видел смертельно опасные проблемы взаимной рекурсии, которые нужно решать, используя что-то вроде require вместо use илиINIT{} вместо BEGIN{}.Но это довольно редко.

Рассмотрим эту программу:

% cat sto-INIT-eg
#!/usr/bin/perl -l
print               "    PRINT: main running";
die                 "    DIE:   main dying\n";
die                 "DIE XXX /* NOTREACHED */";
END         { print "1st END:   done running"    }
CHECK       { print "1st CHECK: done compiling"  }
INIT        { print "1st INIT:  started running" }
END         { print "2nd END:   done running"    }
BEGIN       { print "1st BEGIN: still compiling" }
INIT        { print "2nd INIT:  started running" }
BEGIN       { print "2nd BEGIN: still compiling" }
CHECK       { print "2nd CHECK: done compiling"  }
END         { print "3rd END:   done running"    }

Только при компиляции выдает:

% perl -c sto-INIT-eg 
1st BEGIN: still compiling
2nd BEGIN: still compiling
2nd CHECK: done compiling
1st CHECK: done compiling
sto-INIT-eg syntax OK

При компиляции и выполняется, это выдает:

% perl sto-INIT-eg 
1st BEGIN: still compiling
2nd BEGIN: still compiling
2nd CHECK: done compiling
1st CHECK: done compiling
1st INIT:  started running
2nd INIT:  started running
    PRINT: main running
    DIE:   main dying
3rd END:   done running
2nd END:   done running
1st END:   done running

И оболочка сообщает о выходе 255, за die.

Вы должны быть в состоянии организовать соединение, когда вам нужнодаже если BEGIN{} окажется слишком рано.

Хм, только что вспомнил.Нет шансов, что вы что-то делаете с DATA в BEGIN{}, не так ли?Это не настроено, пока не работает переводчик;он не открыт для компилятора.

5 голосов
/ 24 июля 2014

Хотя остальные ответы верны, я считаю также целесообразным упомянуть использование блоков BEGIN и END при использовании переключателей -n или -p для Perl.

С http://perldoc.perl.org/perlmod.html

Когда вы используете ключи -n и -p для Perl, BEGIN и END работают так же, как в awk, как вырожденный случай.

Для тех, кто не знаком с переключателем -n, он говорит Perl обернуть программу:

while (<>) {
    ...  # your program goes here
}

http://perldoc.perl.org/perlrun.html#Command-Switches, если вас интересует более конкретная информация о переключателях Perl.

В качестве примера, демонстрирующего использование BEGIN с переключателем -n, этот однострочный Perl перечисляет строки команды ls:

ls | perl -ne 'BEGIN{$i = 1} print "$i: $_"; $i += 1;'

В этом случае BEGIN -блок используется для инициализации переменной $i, установив ее в 1 перед обработкой строк ls. Этот пример выведет что-то вроде:

1: foo.txt
2: bar.txt
3: program.pl
4: config.xml
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...