Как работи синтаксисът за използване на Perl?

Примерен код:

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? Ще помогне, ако някой може да го контрастира с директивата #include на C.


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
@Eric: да, това е всичко.   -  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 предоставя), така че програмите, които използват модули, да могат да решат какво от къде да импортират.

Също така ми се струва, че изследвате около краищата на OO.

Освен това:

  • Избягвайте да използвате $a и $b като имена на променливи, защото е лесно да ги объркате с пакетните променливи $a и $b, използвани от sort.

  • Не използвайте малки имена на модули: Те са запазени за pragmata.

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

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