Автор: Andrew Shitov
43. Массивы как аргументы функций в Perl 6
В Perl 6 передавать массивы функциям одно удовольствие. Достаточно объявить массив в сигнатуре, и Perl поймет, что с этим делать дальше. В том числе, если дальше идут, например, скаляры.
sub f($a, @b, $c) { say "a = $a"; say "b = @b[]"; say "c = $c"; } my @arr = <5 7 9>; f(10, @arr, 20);
Легко видеть, что все аргументы передались как следует, без перемешивания:
a = 10 b = 5 7 9 c = 20
Очень удобно, если сравнивать с Perl 5.
Но теперь возникает другой вопрос: а как передать переменное число скалярных величин так, чтобы они все оказались в одном массиве? Ответ: с помощью звездочки, которая создает так называемый slurpy-параметр.
sub g(*@data) { say @data; } g(3, 5, 7);
Эта программа работает правильно:
[3 5 7]
А вот если не поставить звездочку, то возникнет ошибка, поскольку ожидается один массив, а получены три скаляра:
===SORRY!=== Error while compiling sub-slurpy.pl Calling g(Int, Int, Int) will never work with declared signature (@data) at sub-slurpy.pl:5 ------> <BOL>⏏g(3, 5, 7);
Освещенное дерево
42. Фейзеры ENTER и LEAVE в Perl 6
Блоки, которые помечаются большими буквами, в Perl 6 называются фейзерами (phasers). Вы знакомы с ними по Perl 5, например: BEGIN и END.
Сегодня мы рассмотрим два фейзера, которые выполняются при входе в подпрограмму и при выходе из нее: ENTER и LEAVE. В следующем примере это наглядно видно:
sub f() { ENTER say 'Hi'; LEAVE say 'Bye'; say 'Body'; } f;
Программа печатает такие строки:
Hi Body Bye
То есть сначала выполняется блок ENTER, затем тело функции и наконец блок LEAVE.
Порядок выполнения фейзеров не зависит от того, где они расположены. Например, все работает даже после return:
sub f() { say 'Body'; return; LEAVE say 'Bye'; ENTER say 'Hi'; }
При наличии более одного фейзера того же типа, блоки ENTER выполняются в порядке объявления, а LEAVE в противоположном:
sub f() { say 'Body'; ENTER say 'Hi 1'; ENTER say 'Hi 2'; LEAVE say 'Bye 1'; LEAVE { say 'Bye 2'; } } f;
(Здесь одновременно показан пример с блоком кода в одном из фейзеров.)
Программа печатает строки в следующем порядке:
Hi 1 Hi 2 Body Bye 2 Bye 1
41. Передача именованных аргументов в Perl 6
Функции (и методы классов) в Perl 6 способны принимать именованные параметры. Простейший способ — разделить имя и значения стрелкой:
sub f(:$a, :$b) { $a ** $b } say f(a => 2, b => 3); # 8
Но возможен и другой синтаксис, с помощью двоеточия. Существует несколько основных разновидностей.
:key(value)
Все начинается с двоеточия, а значения стоят в скобках:
say f(:a(2), :b(3));
Вместо круглых скобок допустимы, например, угловые:
say f(:a<2>, :b<3>);
:Nkey
Для целочисленных величин предусмотрен еще вот такой странный синтаксис:
say f(:2a, :3b);
:key и :!key
Если именованные параметры используются как флаги, то значения указывать не обязательно. В этом случае передать True и False можно следующим образом:
sub g(:$key) { $key } say g(:key); # True say g(:!key); # False
Пары
Фактически, все показанные варианты создают пары ключ — значение, поэтому таким же образом можно поступить при создании хешей, например:
my %data = :alpha(10), :beta(20), :gamma(30); say %data<beta>; # 20
40. Предопределенные символьные классы в регексах Perl 6
Давайте сегодня посмотрим символьные классы, которые доступны в регексах Perl 6.
Прежде всего, шпаргалка о том, как матчить строку:
'string' ~~ /str/;
Сегодня рассмотрим символьные классы, которые записываются с обратным слешем. Часть из них совпадает с тем, что есть в Perl 5.
Белое на белом
Существует несколько классов, совпадающих с разными видами пробельных символов:
\s — любой пробельный символ, как горизонтальный, так и вертикальный;
\h — горизонтальный пробел (например, пробел или табуляция);
\v — вертикальный пробел (например, перевод строки);
\n — перевод строки;
\t — табуляция.
Обратите внимание, как работают символы при сравнении с переводом строки:
say 1 if "\n" ~~ /\s/; # 1 say 2 if "\n" ~~ /\v/; # 2 say 3 if "\n" ~~ /\h/; # не совпало
Ко всем классам существуют комплиментарные, которые записываются с большой буквой: \S, \H, \V, \N и \T — все они совпадают с соответствующими типами не-пробелов.
Черное на белом
Пара символьных классов, совпадающих с конкретными наборами символов:
\d — цифры (не только ASCII, но и все юникодные).
\w — символы, которые могут быть в словах — буквы, цифры и символ подчеркивания.
Классы с противоположным смыслом:
\D — не цифры;
\W — не то, из чего состоят слова.
Пирожок с маком
39. 0 but True в Perl 6
В Perl 5 был стандартный прием, когда из нуля требовалось сделать логическую истину:
# Perl 5
my $value = '0 but true';
say 2 + $value; # 2
say 'OK' if $value; # OK
В Perl 6 такие трюки можно делать без привлечения строк — для этого есть инфиксный оператор but:
my $v = 0 but True; say $v; # 0 say ?$v; # True
Этот оператор подмешивает к объекту метод, имя которого совпадает с типом значения. В данном примере True это Bool, поэтому переменная $v получает метод Bool, возвращающий True. Теперь в булевом контексте (явно или неявно) переменная окажется истиной, хотя ее числовое значение продолжает быть нулем.
Аналогично можно подмешивать другие типы. Например, сказать, что число пи в виде строки это «примерно три»:
my $p = pi but 'примерно три'; say 0 + $p; say "Значение равно $p";
Здесь сложение с нулем важно, потому что иначе say попытается сразу преобразовать переменную в строку. Программа печатает следующее:
3.14159265358979 Значение равно примерно три
Нужно быть осторожным, если потребуется вывести значение с помощью метода perl. В текущей реализации это не работает, и альтернативное значение теряется:
$ perl6 -e'(0 but True).perl.say' 0
Я предложил вариант решения этой проблемы, посмотрим, что скажут основные разработчики.
38. Тип данных Bag в Perl 6
Тип данных Bag — новый тип данных, которого не было в Perl 5.
Его можно рассматривать как контейнер, который, с одной стороны, знает, сколько отдельных элементов в нем лежит, а с другой, может сказать, сколько там разных видов товара. Можно описать этот тип иначе: Bag это хеш, где по умолчанию значения для добавляемых ключей равны единице. Давайте разбираться на примерах.
Положим в сумку единицу и посмотрим, что про нее знает перл:
my $b1 = bag(1); say $b1.perl;
Программа печатает такой ответ:
(1=>1).Bag
То есть, у нас есть одна единица.
А если положить еще двоечку:
my $b2 = bag(1, 2); say $b2.perl;
Теперь там одна единица и одна двойка:
(1=>1,2=>1).Bag
ОК, а если добавить еще единицу?
my $b3 = bag(1, 2, 1); say $b3.perl;
Теперь их две:
(1=>2,2=>1).Bag
Отвлечемся на секунду: все показанные примеры можно записать и без скобок:
my $b1 = bag 1; my $b2 = bag 1, 2; my $b3 = bag 1, 2, 1;
Более типично, наверное, хранить не числа, а строки, например:
my $cars = bag <green black blue black white>;
Что можно узнать про содержимое переменной $cars?
Во-первых, какие там присутствуют цвета:
say $cars.keys; # (white blue black green)
Во-вторых, сколько, собственно, разных цветов:
say $cars.elems; # 4
Либо сколько разных объектов:
say $cars.total; # 5
Наконец, при создании объектов типа Bag допустимо использовать синтаксис, аналогичный тому, как создаются хеши (правда, скобок и кавычек избежать не удастся):
my $cars2 = bag('green' => 1, 'black' => 2, 'blue' => 1, 'white' => 1);
37. Обращающий метаоператор R в Perl 6
В Perl 6 есть метаоператор R, который меняет операнды у другого оператора. Как и другие метаоператоры, R модифицирует поведение одного из уже существующих операторов, как встроенных, так и определенных пользователем.
1
Проще всего начать с элементарного примера.
say 5 R- 3; # -2
Без R получилось бы 2, а с ним запись 5 R- 3 равносильна 3 - 5.
Еще один пример, с оператором %%, который сообщает, делимо ли одно число на другое.
say 3 R%% 10; # False say 3 R%% 12; # True
2
Можно попробовать применить метаоператор в выражении с двумя и более операциями. Например:
say 10 R- 20 R- 40; # 10
Здесь в обоих случаях стоит оператор минус, так что в итоге мы получаем 40 - (20 - 10).
Подобные конструкции c R вполне можно завернуть в оператор редукции. Предыдущий пример более наглядно (если это слово здесь применимо) записывается так:
say [R-] 10, 20, 40; # 10