perl6.suОсвоим perl6 к 2015 году!

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 ";
	}
ghi ghi 
abc abc 
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()
def

       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;
	}
5
3
1
4
2

       Если определили, что рутина валентна одним массивом, то ровно один массив и можно дать. Ну или на худой конец список <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;
	}
triginta
decem
viginti
---------
sexaginta
quadraginta
quinquaginta

       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);
	# могут обрабатываться
	# разными способами
	}
115 ? 992 = 1107 (угадай какое было действие)
737 ? 254 = 483 (угадай какое было действие)

       Получилась программа развития устного счета.
       А вот интересно стало. Если есть, допустим, список возможных действий <+ - * / ** 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;
	}
me
mihi
ego
me
mei

       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: 8 Oct 2012


       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   

    RSS     stdin@perl6.su    © Alexius Karmanov, 2010-2011