
sub
Как же теперь работают подпрограммы (процедуры, функции) в perl 6? А субрутины работают так.
| | #!/usr/bin/perl6
yellow;
sub yellow {
"We all live in yellow subroutine".say;
} | | We all live in yellow subroutine |
Bene. Пока ничего неожиданного. А если вызвать субрутину с переменной?| | #!/usr/bin/perl6
yellow('small');
sub yellow($epit) {
# my $epit=shift; # теперь так не получается
"We all live in $epit yellow subroutine".say;
} | | We all live in small yellow subroutine |
Сразу после названия субрутины в скобках мы определяем переменные, которыми "валентна" данная субрутина. Можно определить несколько входных переменных.| | #!/usr/bin/perl6
yellow('small','sleep');
sub yellow($epit,$act) {
"We all live and $act in $epit yellow subroutine".say;
} | | We all live and sleep in small yellow subroutine |
Определение входных переменных довольно гибко, там много разных хитростей. Будем пробовать их потом. # mutatio postrema: 14 Aug 2010
» или >> очень хорош в плане разгрузки программы от циклов. Допустим, у нас есть массив, с каждым элементом которого надо проделать какие-то сложные вещи.
| | #!/usr/bin/perl6
my @list=<abc def ghi>;
@list».&double.say;
sub double($in) { # в субрутину заключаем "сложную вещь"
return "$in $in ";
} | | abc abc def def ghi ghi |
Теперь основная часть программы избавится от множества for {} Наша программа продублировала слова. say успешно вывела массив. И опять нас не устраивает, что всё в одну строку. Чуть подправим...| | #!/usr/bin/perl6
my @list=<abc def ghi>;
@list».&double».say;
sub double($in) { # в субрутину заключаем "сложную вещь"
return "$in $in ";
} | | abc abc
ghi ghi
def def |
То есть после названия субрутины тоже можно ставить волшебный » Не знаю почему, но надо писать именно '&double' при вызове, а не 'double' (что приводит к ошибке "метод не найден"). # mutatio postrema: 8 Sep 2010
В "Using perl 6" прочитал, что если субрутина сама находится в блоке, то вне блока она недоступна.
| | #!/usr/bin/perl6
# &socool('boy'); # приводит к ошибке "субрутина не найдена"
{
sub socool($a) {
"$a is so cool!".say;
}
&socool('girl');
} | | girl is so cool! |
Если надо, то можно к 'sub' добавить 'our'. Но в версии ракудо 2012.01 это почему-то не работает. Важно, заканчивается блок точкой с запятой или нет. Если без точки с запятой, то следующая строка может как-то продолжить действие блока.| | #!/usr/bin/perl6
say
{return 'abc'};
~ 'def';
say
{return 'abc'}
~ 'def'; | | Block.new()
_block1016def |
N.B. Внимателно следить за фигурными скобками. Точка с запятой после фигурной скобки может изменить смысл. # mutatio postrema: 30 Jan 2012
В предыдущей ноте в субрутине нам пришлось вводить промежуточную переменную $tmp, потому что $num, которой присваивается значение при вызове функции, readonly. Однако "биндинг" переменных можно настраивать. Есть возможность снять readonly.
| | #!/usr/bin/perl6
my $site='perl6.su';
say routine($site);
say $site;
sub routine($in is rw) {
$in="http://$in";
return $in;
} | | http://perl6.su
http://perl6.su |
Verumtamen нельзя будет вызвать так: routine('perl6.su') Биндинг слишком сильный, как оказывается. Меняя в субрутине $in, мы меняем и $site. Вызывая routine('perl6.su'), мы потом меняем 'perl6.su', а поменять этот объект нельзя, ибо "что написано пером..." # mutatio postrema: 18 Sep 2010
В предыдущей ноте мы жаловались на то, что нельзя вызвать функцию с константой, если потом эту константу предстоит менять. Мы там пользовались "is rw", но можно заменить это на "is copy".
| | #!/usr/bin/perl6
my $perlversion=5;
say routine($perlversion);
say $perlversion;
say routine(5);
sub routine($num is copy) {
$num++;
return "http://perl$num.su";
} | | http://perl6.su
5
http://perl6.su |
"$num is copy" создает копию объекта, который можно менять и изменения которого не повлияют на оригинальный объект. Похоже на то, что "is copy" значительно удобнее "is rw", потому что не надо будет бояться вызвать потом рутину с константой. Хотя, конечно, в плане производительности создание нового объекта менее предпочтительно. # mutatio postrema: 18 Sep 2010
Каким образом можно передать рутине массив?
| | #!/usr/bin/perl6
my @list=<одна муха меня совсем уже заела>;
routine(@list);
@list.&routine; # ну или так
sub routine(@words) {
@words».say;
} | | меня
уже
муха
одна
заела
совсем
уже
одна
заела
меня
совсем
муха |
Понятно. А если передать не массив, а два массива? Хэш? Скаляр?| | #!/usr/bin/perl6
my @list=<decem viginti triginta>;
my @list2=<quadraginta quinquaginta sexaginta>;
# routine(@list,@list2); # ошибка "..., а ожидался один"
my %hash=<десять decem шестьдесят sexaginta>;
# routine(%hash); # ошибка "..., а получила хэш"
my $text='Aquila non captat muscas';
# routine($text); # ошибка "..., а дали строку"
routine(<1 2 3 4 5>);
# routine(6,7,8,9,10); # ошибка "ну что вы опять мне дали!"
sub routine(@words) {
@words».say;
} | | 3
2
1
4
5 |
Если определили, что рутина валентна одним массивом, то ровно один массив и можно дать. Ну или на худой конец список <1 2 3 4 5>, который суть один объект. (6,7,8,9,10) - пять объектов. Если надо передавать два массива, то так и пишем:| | #!/usr/bin/perl6
my @list=<decem viginti triginta>;
my @list2=<quadraginta quinquaginta sexaginta>;
routine(@list,@list2);
sub routine(@l1,@l2) {
@l1».say;
'---------'.say;
@l2».say;
} | | viginti
decem
triginta
---------
quinquaginta
quadraginta
sexaginta |
Bene. # mutatio postrema: 18 Sep 2010
Субрутина может быть валентна перечнем разных объектов: скаляров, массивов, хэшей и даже другими субрутинами.
| | #!/usr/bin/perl6
my @list=1 .. 5;
say routine(@list,sub (@quid) {return [+] @quid});
say routine(@list,sub (@quid) {return [*] @quid});
sub routine(@in,&act) {
return "Итого: " ~ act(@in);
} | | Итого: 15
Итого: 120 |
Это действительно удобно в плане избавления от повторяющихся участков кода. Допустим, мы выносим в одну субрутину сложный и большой алгоритм чего-то. Но в этом алгоритме, скажем, есть две переменные, с которыми можно производить разные действия в разных ситуациях. Можно использовать условные операторы, можно продублировать субрутины, можно использовать eval, но можно и передавать требуемые действия при вызове субрутины.| | #!/usr/bin/perl6
my $res=routine(sub ($a,$b) {$a+$b});
say "$res (угадай какое было действие)";
$res=routine(sub ($a,$b) {$a-$b});
say "$res (угадай какое было действие)";
sub routine(&do) {
# здесь большой
# и сложный алгоритом
# в одном месте которого
# две переменные
my $rand1=1000.rand.floor;
my $rand2=1000.rand.floor;
print "$rand1 ? $rand2 = ";
do($rand1,$rand2);
# могут обрабатываться
# разными способами
} | | 887 ? 826 = 1713 (угадай какое было действие)
655 ? 627 = 28 (угадай какое было действие) |
Получилась программа развития устного счета. А вот интересно стало. Если есть, допустим, список возможных действий <+ - * / ** log>, то как можно его прикрутить к данной программе без использования условий? # mutatio postrema: 18 Sep 2010
Вполне может быть такая ситуация, что будущие аргументы субрутины мы держим в одном массиве. Вместо того, чтобы писать routine(@list[0],@list[1]... можно использовать |@list. Предположим, что наша субрутина выводит на печать латинские падежи:
| | #!/usr/bin/perl6
my @list=<ego mei mihi me me>;
routine(@list[0],@list[1],@list[2],@list[3],@list[4]); # можно так
routine(|@list); # а можно и так
# routine(@list); # так нельзя, потому что ожидается пять скаляров
sub routine($nom,$gen,$dat,$acc,$abl) {
say "N.\t$nom\nGen.\t$gen\nDat.\t$dat\nAcc.\t$acc\nAbl.\t$abl";
} | | N. ego
Gen. mei
Dat. mihi
Acc. me
Abl. me
N. ego
Gen. mei
Dat. mihi
Acc. me
Abl. me |
Интересно, а как сделать наоборот? То есть когда рутина ждет один массив, а у нас в кармане только пять скаляров.| | #!/usr/bin/perl6
my ($nom,$gen,$dat,$acc,$abl)=<ego mei mihi me me>;
routine(($nom,$gen,$dat,$acc,$abl));
sub routine(@list) {
@list».say;
} | | mihi
mei
ego
me
me |
Ergo надо просто добавить еще одну пару круглых скобок, что превращает пять скаляров в один ожидаемый массив. # mutatio postrema: 22 Sep 2010
Какие-то параметры рутины нам может захотеться сделать необязательными при вызове. Имеется дефолтное, и ладно...
| | #!/usr/bin/perl6
marine;
marine('blue');
sub marine($color='yellow') {
say "I live in $color submarine";
} | | I live in yellow submarine
I live in blue submarine |
Ну или просто добавить знак вопроса после имени переменной.| | #!/usr/bin/perl6
marine;
marine('blue');
sub marine($color?) {
say "I live in $color submarine";
} | | I live in submarine
I live in blue submarine |
Просто и со вкусом. Все мы живем в любой подводной лодке... # mutatio postrema: 22 Sep 2010
Admodum может быть такая ситуация, что легче сделать лишний десяток-другой ударов по клавиатуре, чем ползать и искать ожидаемый рутиной порядок параметров.
| | #!/usr/bin/perl6
распорядок('дежурный_по_столовой' => 'Гарифулин', 'повар' => 'Иванов-Петров',
'контролёр' => 'Маневич', 'хлеборез' => 'Наливайко',
'уборщица' => 'Мордоворотов');
sub распорядок($дежурный_по_столовой,$повар,$уборщица,$хлеборез,$контролёр) {
say "Назначить на октябрьские календы следующий
РАСПОРЯДОК
Менеджер по нарезке хлеба: $хлеборез
Менеджер по тепловой обработке продуктов: $повар
Менеджер по соблюдению порядка: $дежурный_по_столовой
Менеджер по качеству: $контролёр
Менеджер по дизайну помещения столовой: $уборщица
Дата: Подпись:";
} | | Назначить на октябрьские календы следующий
РАСПОРЯДОК
Менеджер по нарезке хлеба: хлеборез Наливайко
Менеджер по тепловой обработке продуктов: повар Иванов-Петров
Менеджер по соблюдению порядка: дежурный_по_столовой Гарифулин
Менеджер по качеству: уборщица Мордоворотов
Менеджер по дизайну помещения столовой: контролёр Маневич
Дата: Подпись: |
Bene. # mutatio postrema: 30 Jan 2012
Мы можем ограничить себя от возможных ошибок, связанных с перепутанными параметрами рутины, однозначно определив, что вызывать рутину можно только назвав ее параметры по именам, а не просто перечислив их. Для этого применим двоеточие.
| | #!/usr/bin/perl6
potiones('1.X.2010',thea=>'с бегемотом');
potiones(thea=>'со слоном','2.X.2010');
potiones('3.X.2010',limonada=>'Буратино',coffea=>'С цикорием',sucus=>'Мультиовощ');
# potiones('4.X.2010','водка','Колокольчик'); # так нельзя, потому что
# ожидается только один позиционный параметр (дата)
# остальные - строго именованные
sub potiones($datum,:$spiritus,:$limonada,:$thea,:$coffea,:$sucus) {
# всё кроме даты - факультативно
say "МЕНЮ напитков на $datum
Спиртное: $spiritus
Лимонад: $limonada
Чай: $thea
Кофе: $coffea
Сок: $sucus
-----------------------
"
} | | МЕНЮ напитков на 1.X.2010
Спиртное:
Лимонад:
Чай: с бегемотом
Кофе:
Сок:
-----------------------
МЕНЮ напитков на 2.X.2010
Спиртное:
Лимонад:
Чай: со слоном
Кофе:
Сок:
-----------------------
МЕНЮ напитков на 3.X.2010
Спиртное:
Лимонад: Буратино
Чай:
Кофе: С цикорием
Сок: Мультиовощ
-----------------------
|
'Any()' не очень хорошо вписывается. Поэтому чуть меняем.| | #!/usr/bin/perl6
potiones('1.X.2010',thea=>'с бегемотом');
potiones(thea=>'со слоном','2.X.2010');
potiones('3.X.2010',limonada=>'Буратино',coffea=>'С цикорием',sucus=>'Мультиовощ');
sub potiones($datum,:$spiritus is copy,:$limonada is copy,
:$thea is copy,:$coffea is copy,:$sucus is copy) {
# всё кроме даты - факультативно и теперь меняемо
for $spiritus,$limonada,$thea,$coffea,$sucus {$_ or $_='-'}
say "МЕНЮ напитков на $datum
Спиртное: $spiritus
Лимонад: $limonada
Чай: $thea
Кофе: $coffea
Сок: $sucus
-----------------------
"
} | | МЕНЮ напитков на 1.X.2010
Спиртное: -
Лимонад: -
Чай: с бегемотом
Кофе: -
Сок: -
-----------------------
МЕНЮ напитков на 2.X.2010
Спиртное: -
Лимонад: -
Чай: со слоном
Кофе: -
Сок: -
-----------------------
МЕНЮ напитков на 3.X.2010
Спиртное: -
Лимонад: Буратино
Чай: -
Кофе: С цикорием
Сок: Мультиовощ
-----------------------
|
Теперь хорошо. # mutatio postrema: 22 Sep 2010
In proxima nota мы могли заметить, что при вызове рутины со строго именованными параметрами эти параметры необязательны. Если не указано spiritus=>'пиво', то и ладно. N.B. Позиционные параметры по умолчанию обязательны. Именованные - нет. Предположим однако, что в нашу вымышленную столовую нагрянул ревизор и заставил в обязательном порядке каждый день предлагать напиток каждого рода. Тогда, чтобы и на программном уровне исключить ошибку, мы подправили рутину, добавив восклицательный знак после имён переменных.
| | #!/usr/bin/perl6
# potiones('1.X.2010',thea=>'с бегемотом'); # ошибка "требуется $spiritus"
potiones('1.X.2010',limonada=>'Буратино',coffea=>'С цикорием',
sucus=>'Мультиовощ',spiritus=>'Водка',thea=>'Черный байковый');
sub potiones($datum,:$spiritus!,:$limonada!,:$thea!,:$coffea!,:$sucus!) {
say "МЕНЮ напитков на $datum
Спиртное: $spiritus
Лимонад: $limonada
Чай: $thea
Кофе: $coffea
Сок: $sucus
-----------------------
"
} | | МЕНЮ напитков на 1.X.2010
Спиртное: Водка
Лимонад: Буратино
Чай: Черный байковый
Кофе: С цикорием
Сок: Мультиовощ
-----------------------
|
Теперь посетители обеспечены хорошим выбором. # mutatio postrema: 22 Sep 2010
Предположим, что у нас есть рутина, принимающая переменные $nomen и $familia. И в основном коде у нас тоже есть такие же переменные. Мы вызываем рутину, но не помним в каком порядке она принимает параметры. Можно сделать вызов с 'nomen=>$nomen', а можно и короче.
| | #!/usr/bin/perl6
my $familia='Karmanov';
my $nomen='Alexius';
my $email='stdin@perl6.su';
routine($email,$nomen,$familia);
# routine(:$email,:$nomen,:$familia); # эту возможность поломали в 2012.01, поэтому ерунда
sub routine($familia,$nomen,$email) {
"$nomen $familia habet e-mail ($email), ergo homo sapiens est.".say;
} | | Alexius stdin@perl6.su habet e-mail (Karmanov), ergo homo sapiens est. |
Понятно, что для обозначения субстанций того или иного смысла используются типовые названия: $file, $name, $age, $email, $id etc. Поэтому такая ситуация вполне себе штатная. # mutatio postrema: 22 Mar 2012
| процедуры, :, >>, массивы, our, sub | charta situs | nota XIII, nota LII, nota LXVI, nota LXIX, nota LXX, nota LXXII, nota LXXIII, nota LXXIV, nota LXXV, nota LXXVI, nota LXXVII, nota LXXVIII, nota LXXIX |
|