facebook LinkedIn
Google Book Search
Прегледи

Увод в програмирането на Perl Пакети и модули

от Курс за ССОК

Увод в програмирането на Perl
Лекция #10 Пакети и модули

В допълнение на вградените функции има и доста много пакети и модули, лесни за сваляне и готови за употреба (макар някои да са alpha версия) на http://www.cpan.org, така че не преоткривайте колелото, освен ако наистина не се налага ;) Видимостта на променливите е важен аспект от всяка програма. В Perl има три видими области (scopes): глобална, лексикална и динамична. Променливите, ползвани дотук, бяха глобални. Глобалните променливи могат да бъдат манипулирани навсякъде из програмата. Променливи, дефинирани без ключови думи, са автоматично глобални променливи. Лексикална променлива е такава, която съществува само в блока, в който е дефинирана. Тези променливи са достъпни за блока, в който са дефинирани. Динамичните променливи са видими в блока, в който са дефинирани и са достъпни за подпрограмите, които се викат от този блок. За дефиниране на видимост се ползват ключовите думи my, our, и local. Думата our явно дефинира една променлива като глобална. Думата my дефинира лексикална променлива. Думата local дефинира динамична променлива:

#!/usr/bin/perl

use warnings;

print “Without globals: \n”;
sub1();

our $x = 7;
our $y = 17;

print “\nWith globals: ”;
print “\nglobal \$x: $x”;
print “\nglobal \$y: $y\n”;
sub1();
print “\nglobal \$x: $x”;
print “\nglobal \$y: $y\n”;

sub sub1 {
		my $x = 10;
		local $y = 5;
		print “\$x in sub1: $x\t(lexical to sub1)\n”;
		print “\$y in sub1: $y\t(dynamic to sub1)\n”;
		sub2();
}

sub sub2 {
		print “\$x in sub2: $x\t(global)\n”;
		print “\$y in sub2: $y\t(dynamic to sub1)\n”;
}

Забелязва се, че лексикалните и динамични променливи в една подпрограма скриват глобалните такива (не ги унищожават). Ключовата дума local, приложена за $у, всъщност създава временно копие на глобалната промнелива. Старата стойност е запазена, докато има нова стойност в $у. Когато $у изчезне, се възстановява старата стойност. Не е добра практика да се ползват едни и същи имена, които скриват съответните глобални променливи. В общия случай се препоръчва глобалните променливи да се избягват. Също така, по-добре е една променлива да се подаде на подпрограма, отколкото да се използва local.

Perl използва пакети (packages или namespaces), за да определи достъпността на променливите и подпрограмните идентификатори. Пакетите могат да бъдат използвани за достъп до идентификатори, дефинирани в други файлове, наречени модули. Повечето от правилата за видимост произлизат от концепцията за пакет (package). Всеки пакет си има своя символна таблица, която пази всички променливи и имена на подпрограми на пакета. По подразбиране, глобалните идентификатори в един Perl сорс файл (глобалните променливи и имената на подпрограмите) са част от символната таблица на main пакета. Всъщност, Perl няма истински глобални променливи, а по-скоро пакетни глобални променливи или пакетни променливи. Лексикалните променливи не се поставят в символната таблица на пакета. Всеки блок, в който има някакви локални променливи, си има свой собствен временен “склад” (scratchpad). Символната таблица всъщност е един хеш със идентификатори за ключове и местоположения в паметта за стойности (ключовете на хеша трябва да са уникални, т.е. и променливите трябва да имат уникални имена; @somevar и $somevar са позволени в един пакет, защото имат различни типове). Досега сме работили само с пакета main (това е името на пакета по подразбиране, ако не сме указали такова). Текущият пакет може да се промени чрез package. Създавайки нов пакет, Perl създава и нова символна таблица (празна). Няколко пакета в една програма могат да имат променливи със същите имена без да има конфликт:

#!/usr/bin/perl
#in one file

use warnings;

require FirstPackage;

our $var = “happy”;

print “From main:\n”;
print “\$var = $var\n”;
print “\$main::var = $main::var\n”;

print “\nFrom FirstPackage:\n”;
print “\$FirstPackage::var = $FirstPackage::var\n”;
print “\$FirstPackage::another = $FirstPackage::another\n”;

FirstPackage::displayFirstPackageVars();

FirstPackage.pm

#!/usr/bin/perl

use warnings;

package FirstPackage;
our $var = “birthday”;
my $another = “new year”;

sub displayFirstPackageVars {
		print “\$var in displayFirstPackageVars = ”, $var, “\n”;
		print “\$another in displayFirstPackageVars = ”, $another, “\n”;
}

Ключовата дума require указва на Perl да намери FirstPackage.pm и да го добави към програмата, а Perl търси в текущата директория. Ако файла не може да бъде намерен там, то Perl търси в масива @INC. Тази вградена специална променлива - масив указва къде къде се намират вградените Perl библиотеки на съответната машина. Ключовата дума require предполага разширение .pm при търсене, т.е. няма нужда да указваме .pm в сорс файла. Пълното име (fully qualified name) на една променлива се дава, като се указва първо пакета и след него името на променливата, разделени от :: ($packageName::varName). Taka Perl знае в коя символна таблица да търси съответната променлива.

Модулът е просто пакет, който позволява повече контрол относно как ползвателя на съответния модул може да реферира идентификаторите в пакета на този модул. Плюс на модула е, че разрешава на програмиста да указва идентификаторите да бъдат винаги достъпни до клиентския модул все едно тези идентификатори са оригинално дефинирани в програмата на ползвателя. По този начин идентификаторите могат да бъдат ползвани без да има нужда от пълното им име. Също така, ако искаме да импортваме модули и пакети по време на “компилация”, можем да ползваме use вместо require, който прави това по време на изпълнение. Ползвайки use, Perl може да осигури присъствието на пакета преди изпълнение (иначе може да не се забележи че един пакет липсва докато не се реферира по време на изпълнение):

#!/usr/bin/perl
#program to use our module

use FirstModule;

print “Using automatically imported names:\n”;
print “\@array contains: @array\n”;
greetings();     

FirstModule.pm

#!/usr/bin/perl

package FirstModule;

use Exporter;
our @ISA = qw (Exporter);

our @EXPORT = qw (@array &greetings);

our @array = (1, 2, 3);

sub greetings {
		print “Modules are nice! :~)”;
}

return 1;     #indicates successful import of module

Модулът има няколко допълнителни изисквания освен тези на пакета. Той трябва да има разширение .pm. Ключовата дума require позволява да се дават други разширения (.pl), но use не позволява това. Perl позволява на модулния програмист да експортира идентификатори от един модул за ползване от namespace на друг файл. За да се получи това, трябва съответния модул да е Exporter (той дава функционалността за export на идентификатори за употреба в други файлове). Със израза our @ISA = qw (Exporter); индикираме, че специалната вградена променлива – масив @ISA съдържа Exporter или иначе казано текущия модул е Exporter. Това е пример за наследяване (повече за това по-напред в лекциите). Другият важен момент е този statement: our @EXPORT = qw (@array &greetings); Тук указваме идентификаторите, които нашия модул може да експортира, като добавим идентификаторните елементи към специалната вградена променлива – масив @EXPORT. Така всяка програма, която ползва този модул ще има директен достъп до идентификаторите в този масив без да има нужда от пълното име. return 1; е задължително за модули, които са импортнати със use. Достигането до този код гарантира, че модулът е успешно импортнат, като този statement трябва да връща истина, в противен случай създаваме фатална грешка по време на “компилация”.

Със use може да правим и други интересни неща, като например да указваме версията на Perl, която ни е нужна:

use v5.6.0;

Perl сравнява тази версия със версията на Perl, инсталирана на ползваната машина. Ако версията на машината е по-ниска, генерира се фатална грешка програмата терминира. Аналогично можем да указваме и версията на определен модул, който ползваме:

use someModule 2.0;

Версията на модула пък можем да укажем чрез:

our $VERSION = 2.0;

Освен че може да се импортва цял модул, с Perl можем и да специфицираме части от модул, които да се вкарат:

use FirstModule qw(@array);   #imports only @array

Добра практика е да се импортват само тези идентификатори, който трябват, защото в противен случай можем да “замърсим” съответния namespace. Също така, ако не искаме да импортнем нито един идентификатор можем да използваме празен списък (идентификаторите ще са достъпни чрез пълните им имена). Ключовата дума use си има и противоположност, а именно no. С no можем да “отпратим” указани идентификатори от съответния namespace. По този начин могат да се дефинират подпрограми, който иначе биха били импортвани от друг модул.

Не на последно място по важност са прагмите в Perl. С тях може да се укаже на “компилатора” да ползва разни възможности, които иначе не би ползвал. Две от най-важните прагми са use strict; и use warnings; , като режима strict задължава програмиста да декларира всички променливи като променливи на пакета или като лексикални променливи. Другите задължения са да се ползват кавички около всеки низ и да се вика всяка подпрограма явно. На strict могат да се подават тагове, като например 'vars' и 'subs'. use strict 'vars', поставено в началото на дадена програма проверява дали всички пакетни променливи са указани с пакетното си име (примерно ако имаме една променлива $var, то тя трябва да е $main::var). Има 4 начина една променлива да е валидна за strict. Първият начин е лексикални променливи (дефинирани с my) да бъдат реферирани със краткото си име в блока, в който са дефинирани. Вторият е пакетни променливи в символната таблица (дефинирани с our) да са достъпни до пакета,в който са дефинирани с краткото си име, а във външни пакети да са достъпни със пълното си име. Третият е променливи да бъдат достъпни чрез пълното си име при всички случаи (независимо в или извън техния пакет). Накрая, една програма може да може да укаже use vars; последван от списък с имена на променливи:

use vars qw(var1 var2 var3);

За всяко име се създава глобална пакетна променлива, която е достъпна с краткото си име в текущия пакет. Ако пък укажем use strict 'subs'; , то Perl отказва да се викат подпрограми с bareword. Другата важна прагма, use warnings; (от 5.6 насам), предупреждава за грешки при писане, неинициализирани стойности, както и разни други потенциални проблеми, като нито един от тях не е фатален. Ако пък искаме в част от код да изключим strict, то можем да ползваме no strict; в този блок. Със use също можем да създаваме константи, например:

use constant PI => 3.14159;

След това в кода можем да ползваме PI, вместо числената стойност. Друга интересна прагма е use diagnostics; , която дава по-детайлни съобщения за грешки. Тази прагма може да бъде включвана и изключвана по време на изпълнение на програмата със enable() и disable(). Интерес представлява и прагмата use integer;. Тя указва на “компилатора” да извършва всички аритметични операции като целочислени такива. Това може да се ползва ако имаме дълъг блок от код само със цели числа, където не искаме пред всяка операция да слагаме оператора int.

Заглавен текст

Локални линукс групи RSS
Дискусии