Perl (2 част)

Въвеждане на данни
STDIN, filehandles
Понякога Вие трябва да контактувате с потребителя за да получите информация и да вършите действие с нея. Пробвайте това:
print "Please tell me your name: ";
$name=<STDIN>;
print "Thanks for making me happy, $name !n";
Има нови елементи, които трябва да научите тук. Първо <STDIN> . STDIN е filehandle. Filehandles са неща, които служат за да се взаймодейства с обектите като файлове, сокети, входни данни и др. Може да се каже, че STDIN е стандартната функция за вход. В този случай STDIN чете от клавиатурата (входните данни въведени от потребителя). Този тип скоби - <> чете данни от filehandle. И така ние прочитаме въведените данни от STDIN. Зададената стойност се установява на провенливата $name и принтирани. Някаква идея защо има още един ред. Като натиснете Enter, Вие включвате нов ред във вашите данни. Лесния начин да премахнете новия ред е чрез chop:
chop
print "Please tell me your name: ";
$name=<STDIN>;
chop $name
print "Thanks for making me happy, $name !n"
и този код пропада поради синтактична грешка. Можете ли да познаете защо? Погледнете изведената информация, вижте номера на реда, където е открита грешката и вижте къде в синтаксиса сте сбъркали. Отговорът е в липсващите точка и запетая ( ; ) в края на последните два реда. Ако Вие добавите ; в края на трети ред, но не и на последния ред, тогава програматаще работи както трябва. Това е така защото Perl не се нуждае от ; на последния ред от блок. Препоръчвам винаги да се слага, най-малкото, защото е едно натискане на клавиш, а и след дадения код винаги може да добавите нещо без, ако сте пропуснали ; да търсите къде има синтактична грешка. Функцията chop премахва последния символ от всичко каквото и е дадено. В този случай премахва символа за нов ред. Кодът може да се съкрати, ето как:
print "Please tell me your name: ";
chop ($name=<STDIN>);
print "Thanks for making me happy, $name !";
Скобите ( ) принуждават chop да действа на резултата от това което е вътре в скобите. И така $name=<STDIN> се изпълнява първо, после резултата от това, който е $name със зададена стойност, после от тази стойност се премахва последния символ. Пробвайте без него. Може да прочетете от STDIN колкото пъти поискате. Вижте този код.
print "Please tell me your name: ";
chop ($name=<STDIN>);

print "Please tell me your nationality: ";
chop ($nation=<STDIN>);

if ($nation eq "British" or $nation eq "New Zealand") {
print "Hallo $name, pleased to meet you!n";

} elsif ($nation eq "Dutch" or $nation eq "Flemish") {
print "Hoi $name, hoe gaat het met u vandaag?!n";

} else {
print "HELLO!!! SPEAKEEE ENGLIEESH???n";
}

Премахването е опасно, в период когато най-важното нещо е сигурността при програмирането.
Безопасно премахване чрез chomp
Ние искаме да премахваме само символа за нов ред, а не безогледно, които и да е последен символ. Това може да стане с chomp - който премахва последния символ само, ако той е за нов ред.
chomp ($name=<STDIN>);
И тук Perl гурутата извикват "Открих грешка!". Е добре, chomp не винаги премахва последния символ, ако той е за нов ред, но ако не го премахне, Вие имате специалната променлива, наречена $/ .Като зададете нов символ на тази променлива, тази стойност става символа за нов ред, на която стойността по подразбиране е n.

<span style='font-size:25pt;line-height:100%'>Масиви</span>

Какво са масивите?
В Perl има два типа масиви, асоциативни масиви (хешове) и масиви. И двата масива са списъци. Списъкът с сбор от променливи. Може да мислити за списъците в Perl като за стадо от животни. Списъка се обръща към цялото стадо, а скалара се обръща само към едно животно (Много МУУУУУУ стана,А?). Списъкът е стадо от променливи. Не е задължително променливите да бъдат от един и същи тип. Може да имате три скалара, два масива и три хеша в един масив. Всеки тип от списък си има свое име в Perl. Масивите представляват списък от стойности. Дефиницията на масив в другите езици е: съвкупност от еднотипни елементи (с еднаква големина). В Perl обаче всеки елемент може да е различен и с произволна дължина, следователно масивът е съставен от разнородни по състав елементи. За това често масивът е наричан списък. Масивите се означават с @ - която наподобява "a"(Аrray-масив).
Основи на работата с масиви
За пример, масива е списък от стойности. Списъка може да се обръща към целия списък или към отделни елементи от него. Скрипта по долу задава масива наречен @names . Зададени са 5 стойности в масива.
@names=("Muriel","Gavin","Susanne","Sarah","Anna");

print "The elements of @names are @namesn";
print "The first element is $names n";
print "The third element is $names n";
print 'There are ',scalar(@names)," elements in the arrayn";
Първо, забележете как се декларира масива - @names . Всяка стойност е заградена в кавички "",а запетайката е подразбиращия се разделител за стойности в масива. След това забележете как се извежда. Първо се обръща към себе си като цяло в контекста на списък. Това означава , че се обръща към повече от една стойност. Вижте следващия код
@names=("Muriel","Gavin","Susanne","Sarah","Anna","Paul","Trish","Simon");

print @names;
print "n";
print "@names";
Когато списъка е между "" стойностите в него се извеждат с интервал между тях. Ако искаме да се обърнем към повече от една стойност от масива използваме @ . Когато се обръщате към повече от една стойност от масива, но не към целия масив, това се нарича дял, резен (slice) . Аналогията с кекс е подходяща. Парче от кекс е подходящо сравнение за дял.
Елементите на масива
Масивите не са много полезно нещо ако не можем да се обръщаме към стойностите в него. Първо ако извикваме единичен елемент от масив не трябва да използваме префикса @ който се използва за обръщение към няколко стойности. Ако ни трябва единична стойност, то това ще е скалар, а префикса за скалар е $ . Второ ние трябва да укажем, кой точно елемент искаме. Това е лесно - $array за първия елемент, $array за втория и т.н.. Индексът на списъците започва от 0, докато не направите нещо което да промени това (нещо, което е твърде нежелателно и противопоказно за добро и пълноценно програмиране). И последно, използвахме масива в скаларен контекст за да видим колко елемента има, чрез scalar(@names) в по-горния пример.

Как да се обръщаме към елементите на масив
$myvar="scalar variable";
@myvar=("one","element","of","an","array","called","myvar");

print $myvar; # обръща се към скалар с името myvar
print $myvar; # обръща се към втория елемент на масива myvar
print @myvar; # обръща се към всички елементи на масива myvar
Двете променливи нямат нищо общо помежду си. Ако се върнем към аналогията с животните, това е като да си имаш куче с име 'Myvar' и златна рибка 'Myvar'. Вие никога няма да ги сбъркате, нали?
Номерът на елемента от масив, който извикваме може да бъде променлива.
print "Enter a number :";
chomp ($x=<STDIN>);

@names=("Muriel","Gavin","Susanne","Sarah","Anna");

print "You requested element $x who is $namesn";

print "The index number of the last element is $#names n";
Забележете последния ред на примера. Връща индекса на последния елемент на масива. Същото може да го направите и с $last=scalar(@names)-1; но това не е толкова ефективно. Има по-лесен начин да получите последния елемент на масива, както ще видите:
print "Enter the number of the element you wish to view :";
chomp ($x=<STDIN>);

@names=("Muriel","Gavin","Susanne","Sarah","Anna","Paul","Trish","Simon");

print "The first two elements are @namesn";
print "The first three elements are @namesn";
print "You requested element $x who is $namesn"; # започва от 0
print "The elements before and after are : @namesn";
print "The first, second, third and fifth elements are @namesn";

print "a) The last element is $namesn"; # първи начин
print "b) The last element is @namesn"; # втори начин
Забележете, че имате две стойности разделени от многоточие. Това дава всички стойности от първата до последната между двете числа. означава 0, 1, 2, 3 .Когато за индекс се използва отрицателно число, стойностите на масива се взимат отзад напред, -1 е за последната стойност, -2 за предпоследната и т.н.
За циклите
Всичко дотук е добре, но как ние да изведем всички елементи на масив един след друг? Решението е в следващия пример:
@names=("Muriel","Gavin","Susanne","Sarah","Anna","Paul","Trish","Simon");

for ($x=0; $x <= $#names; $x++) {
print "$namesn";
}

което задава на $x стойност 0, прави един цикъл, прибавя единица към $x , проверява дали е по-малка или равна на $#names , и т.н.. Това е пример за цикъл. За да бъде по-детайлно, цикъла има три части:
• Инициализация
• Проверка на условието
• Модификация
В този случай, променливата $x е инициализирана на 0. След това веднага е проверена дали е по-малка или равна на $#names . Ако е истина, блокът се изпълнява веднъж. Ако условието не е изпълнено и блока не се изпълнява.
Един път след като блока се изпълни, променливата се модифицира. Това е $x++ . След това теста за изпълнение на условието се изпълнява и т.н..
За циклите с .. - оператор за поредица
Има и друг начин за написването на по-горния код:
for $x (0 .. $#names) {
print "$namesn";
}

който използва оператора за поредица .. (две точки една до друга). Това просто задава на $x стойност 0, после увеличава $x с 1 докато стане равно на $#names . През цялото това време изпълнява блока по веднъж на всяко увеличаване с единица.
foreach
За по-добър код използваите foreach .
foreach $person (@names) {
print "$person";
}

Цикъла минава през всеки елемент ('многократно повторение', друго техническо описание на процеса) на масива @names , и всеки елемент се връща като стойност на променливата $person . След това може да извършвате всякакви действия с променливата. Вие може да използвате и:
for $person (@names) {
print "$person";
}
ако искате. Няма разлика като цяло, но кода изглежда малко по-неясен.
Невероятното $_
За да намалим кода , ще Ви преставя $_ , които е входа по подразбиране и шаблонна променлива.
foreach (@names) {
print "$_";
}

Ако Вие не зададете променлива в която да се присвояват като стойности, елементите на масива , $_ се използва по подразбиране за тази и много, много други операции в Perl. Още един пример с функцията print:
foreach (@names) {
print ;
}

Ние не определяме кой елемент да се принтира, затова $_ се изпозва по подразбиране. По дефиниция ако на print не е зададена променлива, тя използва стойността, която се намира в специалната променлива $_.
Преждевременен край на повторения
Процесът на повторенията завършва при постигането на някакво условие зададено първоначално или въобще не завършва. Следващия скрипт е пример за непрекъсваем процес, защото 1 е истина и условието е винаги истана.
while (1) {
$x++;
print "$x: Знаете ли че може чрез натискане на CTRL-C да прекъснете изпълнението на perl скрипт? n";
}

Друг начин да излезете от процеса е, ако при изреждането на елементите на масива намерим този, който ни трябва и продължаването на процеса се обезмисля.
@names=('Mrs Smith','Mr Jones','Ms Samuel','Dr Jansen','Sir Philip');

foreach $person (@names) {
print "$personn";
last if $person=~/Dr /;
}

Оператора last изпълнява функцията прекъсване на процеса и излизане от цикъла. Не се тревожете за /Dr / това е регулярен израз, който ще бъде обяснен по-късно. Всичко, което трябва да знаете засега е, че връща истина ако намери съвпадение между това в скобите и подадената променлива. В случая когато открие съвпадение подава истина и функцията last се изпълнява. Контрол върху функцията last се извършва чрез етикети.
Засега е просто, но почакайте! Ние се нуждаем от лекар, който го има и в списъка с имена, не просто само лекар. Следващия пример е за това:
@names =('Mrs Smith','Mr Jones','Ms Samuel','Dr Jansen','Sir Philip');
@medics =('Dr Black','Dr Waymour','Dr Jansen','Dr Pettle');

foreach $person (@names) {
print "$personn";
if ($person=~/Dr /) {
foreach $doc (@medics) {
print "t$docn";
last if $doc eq $person;
}
}
}

Първо се взима първия елемент на масива @names, изписва се на екрана, и след това се проверява за съвпадение с Dr, ако се открие съвпадение се изпълнява следващия блок. В него се изреждат всички елементи на масива @medics, и ако се открие, че съдържанието на променливата $doc съвпада със съдържанието на променливата $person се прекъсва изпълнението на този блок и се преминава към външния цикъл с нова променлива $person.

Но има малък проблем, ние искаме при намиране на съвпадение да прекъснем изцяло цикъла, а не само изпълнението на вътрешния цикъл. Трябва да укажем на функцията last, кое точно искаме да прекъснем. Това става чрез етикети, като във следващия пример:
@names =('Mrs Smith','Mr Jones','Ms Samuel','Dr Jansen','Sir Philip');
@medics =('Dr Black','Dr Waymour','Dr Jansen','Dr Pettle');

LBL: foreach $person (@names) {
print "$personn";
if ($person=~/Dr /) {
foreach $doc (@medics) {
print "t$docn";
last LBL if $doc eq $person;
}
}
}

Има само две промени тук. Дефинирали сме етикет с името LBL. Когато укажем на last даден етикет, той прекъсва функцията, на която е зададен първоначално този етикет.

Има още две функции за работа с цикли redo и goto. Redo връща на току-що изпълнилото се $_. Goto изпраща към друг цикъл, подпрограма или израз за изчисляване. Не се препоръчва използването му.
Промяна на елементите на масив
И така имаме масива @names. Искаме да го променим. Пробвайте следващия код:
print "Enter a name :";
chomp ($x=<STDIN>);

@names=("Muriel","Gavin","Susanne","Sarah");

print "@namesn";

push (@names, $x);

print "@namesn";
Push функцията прибавя нова стойност към края на масива. Разбира се може да не е само една стойност:
print "Enter a name :";
chop ($x=<STDIN>);

@names=("Muriel","Gavin","Susanne","Sarah");
@cities=("Brussels","Hamburg","London","Breda");

print "@namesn";

push (@names, $x, 10, @cities);

print "@namesn";
Нови функции за работа с масив:

@names=("Muriel","Gavin","Susanne","Sarah");
@cities=("Brussels","Hamburg","London","Breda");

&look;

$last=pop(@names);
unshift (@cities, $last);

&look;

sub look {
print "Names : @namesn";
print "Cities: @citiesn";
}

Сега имаме два масива. pop функцията премахва последния елемент на масив и го връща, което означава че може да извършите някакви действия с върнатата стойност. Функцията unshift прибавя стойност в началото на масив. В таблицата по долу са изборени функциите, които се използват за работа с масив.
push Прибавя стойност към края на масив.
pop Премахва и връща последната стойност на масив.
shift Премахва и връща първата стойност на масив.
unshift Прибавя стойност към началото на масива.

Сега за достъпа до другите елементи на масива. Ще Ви представя функцията splice.
@names=("Muriel","Sarah","Susanne","Gavin");

&look;

@middle=splice (@names, 1, 2);

&look;

sub look {
print "Names : @namesn";
print "The Splice Girls are: @middlen";
}

Първия аргумент на функцията splice е масив. Втората стойност е индекс (число) показващо, коя стойност от списъка (масива) да се използва за начало. В този случай тя е 1 (ще се започне от втория елемент). После идва елемента, който показва колко елементи да се премахнат от масива и да се прибавят към втория.
Ако използвате резултата от splice като скалар:
@names=("Muriel","Sarah","Susanne","Gavin");

&look;

$middle=splice (@names, 1, 2);

&look;

sub look {
print "Names : @namesn";
print "The Splice Girls are: $middlen";
}

тогава скалара взема като стойност последната му подадена стойност. Функцията splice се използва и за триене на елементи от масив.
Триене на променливи от масив
Искаме да изтрием Hamburg от масива. Как да го направим? Може би ето така:
@cities=("Brussels","Hamburg","London","Breda");

&look;

$cities="";

&look;

sub look {
print "Cities: ",scalar(@cities), ": @citiesn";
}

Разбира се Hamburg е премахнат, но забележете, броят на елементите остана един и същ. Все още има 4 елемента в масива. Резултата от горния код е, че $cities, съществува но няма стойност. И така ние трябва да използваме splice функцията за да премахнем елемента изцяло.
splice (@cities, 1, 1);
Сега нека пробваме друго:
$car ="Porsche 911";
$aircraft="G-BBNX";

&look;

$car="";

&look;

sub look {
print "Car :$car: Aircraft:$aircraft:n";
print "Aircraft exists !n" if $aircraft;
print "Car exists !n" if $car;
}
Изглежда че сме изтрили променливата $car. Но не точно. Ние сме изтрили стойността, която тя има, но не и самата променлива. В момента тя има стойност null и затова теста с if пропада. Само ако нещо дава като отговор грешка, то това не означава, че не съществува. Перуката е фалшива коса, но перуката съществува. Вашата променлива е все още там. Perl има функция, която да тества дали нещо съществува. Съществувам, в превод на Perl означава дефиниран (defined):
print "Car is defined !n" if defined $car;
Този код ще върне истина. Примерите дотук поставят въпроса, всъщност как да премахнем невъзвратимо една променлива. Много просто, ето как:
$car ="Porsche 911";
$aircraft="G-BBNX";

&look;

undef $car;

&look;

sub look {
print "Car :$car: Aircraft:$aircraft:n";
print "Aircraft exists !n" if $aircraft;
print "Car exists !n" if $car;
}

Променливата $car е унищожена, тя липсва.
Флагове
Вие знаете, че може да пуснете режима с повече предупреждения и обяснения при грешки с -w от командния ред. Всяка опция на perl, която може да се зададе от командния ред, може да се зададе и в кода на скрипта.
perl script.pl hello
за да изпълни този код:
#!perl -w

@input=@ARGV;

$outfile='outfile.txt';
open OUT, ">$outfile" or die "Can't open $outfile for write:$!n";

$input2++;
$delay=2 if $input eq 'sleep';

sleep $delay;

print "The first element of @input is $inputn";
print OUY "Slept $delay!n";
който ще се стартира по-същия начин , ако го стартирате така:
perl -w script.pl hello
Много по-удобно е да се слагат флагове вътре в скриптовете. Не е задължително да е -w , може да е всеки аргумент който Perl поддържа. Стартирайте
perl -h
за пълния списък на аргументите.
use strict;
Какво е това строгост (strict) и как да я използваме? Модулът strict ограничава 'несигурните, опасните конструкции', съгласно perl документацията. Няма смисъл да се притеснявате за опасни конструкции, ако сте прекарали часове наред в дебъгване на кода си. Когато включите модула strict , има три неща които Perl държи да са зададени точно:
• Променливите 'vars'
• Референциите 'refs'
• Подпрограмите 'subs'
При използването на този модул трябва променливите да се декларират преди използването им, като всяка променлива се дефинира с my . Това е пример за програма, която не използва стриктен режим:
perl script.pl "Alain James Smith";
където "" затваря стринга като единичен параметър, иначе той ще се разглежда като три параметъра.
#use strict; # махнете '#' след като стартирате кода няколко пъти

$name=shift; # дава първия аргумент от масива @ARGV

print "The name is $namen";
$inis=&initials($name);

$luck=int(rand(10)) if $inis=~/^(?:||)/i;

print "The initials are $inis, lucky number: $luckn";

sub initials {
my $name=shift;
$initials.=$1 while $name=~/(w)w+s?/g;
return $initials;
}
Вие вече трябва да разбирате какво върши по-горния код. Когато премахнете '#' пред use strict; и стартирате кода отново, Вие ще получите изход подобен на този:

Global symbol "$name" requires explicit package name at n1.pl line 3.
Global symbol "$inis" requires explicit package name at n1.pl line 6.
Global symbol "$luck" requires explicit package name at n1.pl line 8.
Global symbol "$initials" requires explicit package name at n1.pl line 14.
Execution of n1.pl aborted due to compilation errors.

Тези предупреждения означават, че на Perl не му е ясно какъв е обсега на действие на тези променливи. Това означава, че Вие трябва да декларирате всяка една от тях с my, за да ограничите изпълнението им в дадения блок, в който се използват, или да им се зададе референцията с тяхното пълно име. В долния пример се използват и двата метода:
use strict;

$MAIN::name=shift; # shifts @ARGV if no arguments supplied

print "The name is ",$MAIN::name,"n";
my $inis='';
my $luck='';

$inis=&initials($MAIN::name);

$luck=int(rand(10)) if $inis=~/^(?:||)/i;

print "The initials are $inis, lucky number: $luckn";

sub initials {
my $name=shift;
my $initials;
$initials.=$1 while $name=~/(w)w+s?/g;
return $initials;
}
Използването на my в подпрограма не е нищо ново за вас, а my извън подпрограма вече е. Ако се замислите цялата програма е един блок, така че може да се зададе променливите да се виждат само в този блок. Друга интересна част от кода е $MAIN::name. Това както може би очаквате е пълното име на променливата. Първата част е името на пакета, в случая MAIN (главния, основния). Втората част е името на променливата.