Въведение (1/2)
- Функция и процедура се наричат подпрограма (subroutine)
в Perl. Ние все пак можем да ги наричаме функции
понякога.
- Блок от код е всичко затворено между фигурни скоби
(closure)
- {
# this is a block
}
- Подпрограмата е блок с име, но такъв който се изпълнява
ако бъде “извикан” по име.
- Подпрограмата не се изпълнява, ако изпълнението на
програмата стигне до там, където е дефинирана.
Въведение (2/2)
- Могат да се дефинират навсякъде в програмата:
- sub subname
{
# code comes here
}
- Subroutines могат да имат имена, съвпадащи с тези на
променливите.
- По подразбиране се извикват с празния списък, но не е
задължително да пишем скобите ако не предаваме
параметри.
- Форсиране на контекст за извикване на подпрограма става
с
&. Например:
&function_name
- реално погледнато се извиква
само с името.
Оценка и върната стойност
- Всички дефиниции на подпрограми са глобални, но не е
забранено да се дефинира подпрограма в тялото на
друга.
- Подпрограмите винаги са част от даден израз.
- Изход (връщане) от подпрограма може да се предизвика с
оператор
return.
- С оператор
return се определя и
изразът, оценката на който ще бъде върната като
резултат.
- При липса на
return подпрограмата връща
оценката на последния оценен израз.
- Може да се връща както скалар, така и списък.
Пример
- Извикване на подпрограма и върната стойност
- sub a {
return $a + $b;
}
sub b {
$b + $a;
}
$a = 10;
$b = 20;
print a."\n".b;
Предаване на параметри
- Параметрите се предават като списък
- Параметрите се появяват в подпрограмата в специалния
списък
@_ (реално погледнато името му е
”_”)
- Достъпът до елементите на @_ става както и с другите
списъци -
$_[1], $_[2]
и т.н.
$_ сам по себе си е съвсем друг скалар
и НЕ съдържа първия елемент на
@_
- Когато подпрограмaта се извиква с
&
пред името, но без параметри тя получава текущото
съдържание на @_.
Друг пример
- Друг пример за извикване на подпрограма и върната
стойност
- sub mm {
$max = $min = shift(@_);
foreach (@_) {
$max = $_ if $max < $_;
$min = $_ if $min > $_;
}
return ($min, $max);
}
($leastValue, $bestValue) = mm( qw { 198 123 72 19 231 } );
print $bestValue.' >> '.$leastValue."\n"
Именувани параметри
- Съществува ефективен механизъм за предаване на именувани
променливи с помоща на конструкцията:
- sub_name( "param1" => "value1",
"param2" => "value2" );
- Това е все едно да създадем динамично хеш, да го оценим
в списъчен контекст и да предадем резултата.
- В подпрограмата взимаме параметрите с:
- %params = @_;
- …което реално погледнато отново ги подрежда по
двойки
Специален оператор caller
caller връща информация за мястото и условията, при които е била извикана дадена подпрограма. Няколко форми:
- $package = caller;
- ($package, $filename, $line) = caller;
# кой пакет, от кой сорс файл, кой ред
- ($package, $filename, $line, $subroutine, $hasargs,
$wantarray, $evaltext, $is_require, $hints, $bitmask) = caller($i);
Списъчен или скаларен контекст?
- С помоща на
wantarray се разбира в
какъв контекст е била извикана дадена подпрограма —
списъчен или скаларен
- По този начин можем да моделираме поведението на
подпрограмата спрямо контекста
- sub cleverSub {
return wantarray? (1, 2, 3): 5;
}
$retScalar = cleverSub; # получава стойност 5
@retArray = cleverSub; # получава стойност (1, 2, 3)
Видимост на променливите
- Стриктен режим (strict). Активира се с
помоща на специална pragma (директива
към компилатора).
use strict; # активиране на стриктен
режим
no strict; # деактивиране
- Дефинирането на променливи в Perl не е задължително,
освен ако не работим в strict
“vars” режим.
- Всяка променлива има различна видимост
(scope) в зависимост от това в кой
блок код е дефинирана.
- Дефинирането на променливи в даден блок става с
“my”. Така една променлива
е видима в този блок и останалите под него. Понякога се
наричат “частни” променливи (private
variables) или
“lexical”.
Видимост на променливите: my и strict
- “my” променливите не се
виждат извън блока, в който са дефинирани.
- В no strict “vars” режим
всички променливи, които не са били изрично дефинирани,
получават глобален scope — видими са от всякъде
в програмата.
- В “strict” режим всяка
променлива се дефинира някъде и е видима от там надолу,
т.е. не може да бъде използвана, преди да е
дефинирана.
- Променлива, дефинирана като
“my” в някоя подпрограма, е
видима само за нея и за конструкциите вътре в нея, но не
и от другите подпрограми
Видимост: символна таблица
- Perl програмите могат да бъдат разделени на няколко
“namespace”-а
- Подразбиращият се namespace е
main::
- Глобалните променливи (вкл. дефинираните чрез
our) за всеки namespace се съдържат в
т.нар. символна таблица
- Лексикалните променливи (дефинирани с
my) не попадат в символната
таблица
- Символните таблици са достъпни посредством служебни
hash-ове от вида
%{main::}
- foreach $key (keys %{main::}) {
print "$key\t$main::{$key}\n";
}
Видимост: our
- С помоща на
our дадена променлива става
видима навсякъде, независимо къде е дефинирана.
our позволява дефинирането на глобални
променливи в strict "vars" режим
- Променливите дефинирани с
our обаче са
достъпни само с пълното си име
(включително името на пакета), когато се използват извън
scope-а, в който са дефинирани
Видимост: пример
- Пояснение за видимост на променливи:
- $name = 'Marin';
$verb = 'hapva';
sub testan {
my $name = 'Niki';
print "testan: $name $verb\n";
}
sub othertestan {
my $verb = 'spi';
print "othertestan: $name $verb\n";
}
print "Po princip: $name $verb\n";
testan;
othertestan;
print "I vse pak: $name $verb\n";
Получастни променливи
local ни позволява в даден блок да дадем нова стойност на глобална променлива, като извън този блок тя си запази старата стойност.
- Не можем да локализираме, декларирани с
my, променливи.
- Често се локализира
$_ преди код, който я променя.
- Налага се рядко - в повечето случаи ви трябва
my, а не local.
- our $help = 911;
print "$help\n";
{
local $help;
$help = 150;
print "$help\n";
somebody_help();
}
print "$help\n";
sub somebody_help { print "$help\n"; }
Видимост: примери (1/2)
- use strict;
my @cows_and_chickens;
{
local @cows_and_chickens;
# ГРЕШКА: Can't localize lexical variable @cows_and_chickens…
}
- no strict;
{
local @real_men;
# за разлика от в стриктен режим няма да върне грешка, а ще
# създаде глобална променлива
@real_men и ще я локализира
@real_men = qw { cow chicken};
}
- no strict;
my %buuu;
{
local %buuuu;
# независимо от режима, локализирането
# на частни променливи е строго забранено
}
Видимост: примери (2/2)
-
# числата 1 и 2 ще се появят в
$_
for (1..2) {
# този цикъл също променя $_ и ще свърши
# когато стойността й стане недефинирана
print while (<>);
# вместо да отпечата 1 или 2 ще
# получим празен ред и/или предупреждение
print "$_\n";
}
- for (1..2) {
# отделяме операторите, които променят глобалната
$_ в блок
# и локализираме в него $_
{
local $_;
print while (<>);
}
# по-горе сме си локализирали $_ и стойността й
# не е недефинирана а е 1 или 2
print "$_\n";
}
Прототипи на подпрограми (1/2)
- Прототипите не са задължителни.
- В сила са само при директно извикване на подпрограмата.
- Основната идея е да позволят на собствените подпрограми да работят като вградени оператори.
- Позволяват дефиниране на броя и вида на параметрите, които очаква подпрограмата.
Прототипи на подпрограми (2/2)
- След името на подпрограмата се изброяват в скоби видовете параметри, които очаква тя само със знаците
\, $, %, @, *
sub laro($$);
sub laro(\$$);
- Ако се разделят със запетайка първите указват задължителните, а останалите незадължителните параметри.
- Всеки символ има особеност:
$ - форсира скаларен контекст.
@, % - поема всички аргументи след него.
* - позволява предаване на константи, typeglob-ове, скаларни изрази и т.н.
Анонимни функции
- Наричат се така, защото са отделeни в блок код със
sub, но не са дефинирани явно.
- Позволяват динамичното създаване на код, който да бъде запазен в скалар и извикван в последствие. Мощ.
- $wrong = { print "buu"; }; # грешно!
$code = sub { print "muu"; }; # вярно
$code->();
$code->(1,2);
&$code();
&$code(2,3);
Динамична оценка на кодове блок
- С помоща на
eval може да се оцени даден блок код в реално време. Тоест, в момента на изпълнение на програмата, а не при компилация.
eval BLOCK - оценява блок от код. Синтаксисът се проверява по време на компилация.
eval EXPR - възприема оценката на EXPR като текст на код, който да оцени.
$@ - при наличие на фатална грешка в кода, изпълнен от eval (синтактична, runtime, извикано е die) съдържа текста на грешката. Ако всичко е минало безпроблемно е undef.
eval в ползва на човечеството
- Могат да бъдат прихванати грешки, които иначе биха довели до прекратяване изпълнението на програмата.
eval е в основата на механизма за улавяне на грешки на perl.
- Лесно може да се „сглабя“ динамично код от самата програма. На практика може да си напишем програма, която да генерира други програми и да си ги изпълнява.
Пример за eval
- eval {$answer = $a/$b; } # при компилация?
warn $@ if $@; # деление на нула?
print "Wow, we are still here!!\n";
eval "$a++"; #
$a се интерполира
# ако стойността на $a е била 5
# все едно сме опитали да оценим 5++
# => не се случва нищо
eval '$a++'; # ей това вече се оценява
# по време на изпълнението
# и наистина ще увеличи $a
Използвани източници
- perlsub
- Глава 8 на Learning Perl
- Глави 4 и 6 на Programming Perl