Как работает синтаксис Perl use?

Образец кода:

m1.pm

my $a;
my $b;
sub init {
    $a = shift;
    $b = shift;
}

sub printab {
    print "a = -$a-\n";
    print "b = -$b-\n";
}

1;

m2.pm

my $a;
my $b;
sub init {
    $a = shift;
    $b = shift;
}

1;

test.pl

use strict;
use warnings;

use m1;
use m2;

init('hello', 'world');
printab();

Бежать:

$ perl test.pl
a = --
b = --
$

Что происходит, так это то, что вызов init('hello', 'world') отображается на m2.pm и инициализирует там переменные ($a и $b).

В этом есть смысл, но я не понимаю, почему эти значения недоступны в test.pl.

  • Есть ли что-то принципиально неправильное, что я пытаюсь сделать здесь? Как правильно использовать два модуля с одинаковыми именами подпрограмм и переменных?

  • Как именно работает Perl use? Было бы полезно, если бы кто-нибудь мог сравнить это с директивой C #include.


person Lazer    schedule 10.11.2010    source источник
comment
Зачем мне это нужно, см. эта ветка SO.   -  person Lazer    schedule 10.11.2010
comment
Это ваш полный код? Определяет ли какой-либо из ваших модулей подпрограмму import или наследует от Exporter. Если нет, вы не экспортируете подпрограммы в test.pl.   -  person Eric Strom    schedule 10.11.2010
comment
@ Эрик: да, это все.   -  person Lazer    schedule 10.11.2010
comment
Хорошо, смотрите мой ответ, почему он не работает.   -  person Eric Strom    schedule 10.11.2010
comment
Потому что m1 и m2 говорят, что они свои, и что другие прицелы должны держаться от них подальше.   -  person Axeman    schedule 10.11.2010


Ответы (3)


Во-первых, прочтите perldoc perlmod.

Вы не объявляете пространство имен ни в одном из модулей, поэтому все находится в пространстве имен main. Объявите package m1; в m1.pm и package m2; в m2.pm.

По крайней мере, вы должны реализовать метод import (или унаследовать тот, который предоставляет Exporter), чтобы программы, использующие модули, могли решать, что и откуда импортировать.

Мне также кажется, что вы исследуете границы ОО.

Дальше:

  • Избегайте использования $a и $b в качестве имен переменных, потому что их легко спутать с переменными пакета $a и $b, используемыми sort.

  • Не используйте имена модулей в нижнем регистре: они зарезервированы для прагмат.

Минимальная реализация (все в одном файле для удобства тестирования) выглядит так:

package My::M1;

use strict; use warnings;

sub new { my $class = shift; bless { @_ } => $class }

sub a {
    my $self = shift;
    my ($v) = @_;
    $self->{a} = $v if @_;
    return $self->{a};
}

sub b {
    my $self = shift;
    my ($v) = @_;
    $self->{b} = $v if @_;
    return $self->{b};
}

package My::M2;

use strict; use warnings;
use base 'My::M1';

sub printtab {
    my $self = shift;
    for my $x (qw(a b)) {
        printf "%s = -%s-\n", $x, $self->$x;
    }
}

package main;

my $m = My::M2->new(a => 'hello', 'b' => 'world');
$m->printtab;
person Sinan Ünür    schedule 10.11.2010
comment
Должно ли имя пакета совпадать с именем модуля? (я так не думаю). - person Lazer; 10.11.2010
comment
@Lazer =› Это не обязательно, но по соглашению. - person Eric Strom; 10.11.2010
comment
@Lazer: Нет, технически они не должны быть одинаковыми. Однако экспорты не работают правильно, если они разные, потому что, как сказал Эрик Стром, use Module; неявно выполняет Module->import() после загрузки. Если пакет в Module назван не Module, то Module->import() будет искать не тот пакет и не сможет ничего импортировать. Если вы не экспортируете, то нет причин (кроме соглашения), по которым имя пакета и имя модуля должны совпадать. - person Dave Sherohman; 11.11.2010

В Perl ключевое слово use в точности эквивалентно следующему:

use Mymodule;

#is the same as

BEGIN {
   require Mymodule;
   Mymodule->import();
}

Поэтому, если вы не определяете процедуру импорта в своем коде (или наследуете от Exporter), то ваши модули ничего не импортируют в test.pl.

Как поймал Синан, вы не объявляете пакет в своих модулях, поэтому по умолчанию используется пакет main. В этом случае все ваши подпрограммы находятся в main, но лексические переменные (объявленные с помощью my) ограничены только файлом, в котором они объявлены.

Таким образом, m1 определяет sub init и sub printab, к которым относятся лексики $a и $b. Но затем, когда test.pl загружает m2, подпрограмма init перезаписывается новым определением, которое больше не замыкается вокруг двух лексических выражений. Таким образом, он пишет в переменные пакета $main::a и $main::b вместо лексических переменных, к которым привязан printab.

Если бы у вас были включены предупреждения (что всегда следует делать при обучении), вы бы были предупреждены о переопределении подпрограммы.

Вы должны начинать каждый из своих модулей с:

package Some::Package::Name;
use warnings;
use strict;

а затем заканчивайте каждый модуль:

1;

Это связано с тем, что когда вы use/require используете модуль, он должен возвращать истинное значение в конце, чтобы Perl знал, что он загружен правильно.

person Eric Strom    schedule 10.11.2010

printab() определяется в файле m1.pm и имеет доступ только к переменным $a и $b, которые относятся к этому файлу. Переменные $a и $b в m2.pm привязаны к этому файлу и являются другими переменными, чем $a и $b в m1.pm.

init() устанавливает переменные в области m2.pm (поскольку это последнее место, где была определена функция &init), поэтому она не устанавливает те же самые переменные, которые printab() будет пытаться напечатать.

person mob    schedule 10.11.2010
comment
@DVK - Нет, я имел в виду то, что сказал. Добавление package Foo вверху каждого файла в OP не сделает лексические переменные из одного файла видимыми в другом файле. - person mob; 10.11.2010
comment
правило - будет ли область действия сохраняться за пределами пакета в одном и том же файле? Например. package A; my $var1 = "x"; 1; package B; print $var1; - увидит ли print $var1 переменную, добавленную в пакет A? - person DVK; 11.11.2010
comment
@DVK => переменная my имеет область действия блока (где конец файла представляет собой конец блока), поэтому в вашем примере $var1 отображается в обоих пакетах. Когда я использую более одного пакета в файле, я обычно заключаю все объявление пакета в блок: {package ...; ....}, чтобы лексика не просочилась - person Eric Strom; 11.11.2010
comment
@Eric - тогда я исправляюсь, это действительно файл, а не область пакета. По какой-то причине у меня сложилось впечатление, что пакет enw накладывает новую область действия блока. - person DVK; 11.11.2010
comment
@DVK: нет, вам нужно явно создать новую область вокруг объявления package, если вам это нужно. - person Ether; 11.11.2010