Автор: Andrew Shitov
Дерево на фоне неба
Amsterdam, Netherlands
Zandvoort, Netherlands
67. Модификаторы (adverbs) в регексах Perl 6, часть 3
Осталось рассмотреть три модификатора: :r, :ov и :ex.
:r или :ratchet
Запрет на бектрекинг. Сравните две попытки:
> 123 ~~ / \d+ 3/
「123」
> 123 ~~ m:r/ \d+ 3/
False
Несмотря на то, что сам по себе регекс подходит к числу 123, во втором случае класс \d+ захватил все цифры и отказывается уступать. Более реалистичный случай использования :r вы можете встретить в конструкциях с альтернативами |.
:ov или :overlap
Разрешает поиск с пересечениями. Если в одной позиции начинается более чем одна подходящая последовательность, то выбирается самая длинная. Например, найти все строки, начинающиеся с 1 и заканчивающиеся 2:
> 12345678913234 ~~ m:ov/ 1 .* 2 / (「123456789132」 「132」) > 12345678913234 ~~ m:ov/ 1 .*? 2 / (「12」 「132」)
:ex или :exhaustive
Работает как :ov, но снимает ограничения на поиск самых длинных последовательностей. То есть этот модификатор найдет все:
> 12345678913234 ~~ m:ex/ 1 .* 2 / (「123456789132」 「12」 「132」) > 12345678913234 ~~ m:ex/ 1 .*? 2 / (「12」 「123456789132」 「132」)
66. Модификаторы (adverbs) в регексах Perl 6, часть 2
Сегодня — продолжение обзора модификаторов регексов в Perl 6.
:p(N) или :pos(N)
Начать поиск с указанной позиции. Важно обратить внимание на то, что при использовании этого адверба (как их называть по-русски? наречия вроде не оч в тему, а название модификаторы было в Perl 5) регекс привязывается к этой позиции и не может пропустить другие символы перед тем, как совпасть.
> 'hello world' ~~ /l./ 「ll」 > 'hello world' ~~ m:p(0)/l./ False > 'hello world' ~~ m:p(1)/l./ False > 'hello world' ~~ m:p(2)/l./ 「ll」
Попробуем найти третье вхождение буквы l:
> 'hello world' ~~ m:p(8)/l./ False > 'hello world' ~~ m:p(9)/l./ 「ld」
:c или :continue
Продолжить с места предыдущего совпадения. Рассмотрим на примере с той же строкой:
> 'hello world' ~~ /l./
「ll」
> 'hello world' ~~ /l./
「ll」
> 'hello world' ~~ m:c/l./
「ld」
Аналогично :p, к :c можно добавить целочисленный аргумент, чтобы сместить начало матча в нужную позицию.
65. Модификаторы (adverbs) в регексах Perl 6, часть 1
В регексах Perl 6 по-прежнему есть модификаторы, однако они теперь выглядят и называются иначе. Называются они теперь adverbs (наречия) и ставятся после двоеточия не после, а до регекса.
Adverbs имеют короткое и длинное имена. Давайте посмотрим на имеющиеся предложения. Все приведенные ниже примеры показаны как часть диалога в режиме REPL.
:i или :ignorecase
Игнорирование регистра букв.
> 'Moscow' ~~ m:i/moscow/
「Moscow」
> 'Moscow' ~~ m/moscow/
False
:s или :sigspace
Включение режима значимости пробелов. В целом этот флаг противоположен по действию модификатору /x из Perl 5. По умолчанию пробелы в регексе считаются незначимыми.
> 'alpha beta' ~~ / a\S+ b\S+ / Nil > 'alpha beta' ~~ / a\S+ ' ' b\S+ / 「alpha beta」 > 'alpha beta' ~~ m:s/ a\S+ b\S+ / 「alpha beta」
:g или :global
Искать глобально (но без пересечения) и возвращать несколько результатов.
> 'text' ~~ /\w/
「t」
> 'text' ~~ m:g/\w/
(「t」 「e」 「x」 「t」)
64. Тернарный оператор в Perl 6
В Perl 6 по-прежнему присутствует тернарный оператор, но выглядит он теперь иначе:
my $var = -10; my $abs = $var < 0 ?? -$var !! $var; say $abs;
Остальное все понятно — оператор делает ровно то же, что и ? ! в Perl 5. Однако, при попытке написать по-старому, вы получите сообщение об ошибке:
$ perl6 ternary.pl ===SORRY!=== Error while compiling /Users/ash/ternary.pl Unsupported use of ? and : for the ternary conditional operator; in Perl 6 please use ?? and !! at /Users/ash/ternary.pl:2 ------> my $abs = $var < 0 ?⏏ -$var : $var;
Интересно, что в самых первых реализациях Perl 6 тернарный оператор выглядел как ?? ::. На этот случай тоже предусмотрено сообщение:
$ perl6 ternary.pl ===SORRY!=== Error while compiling /Users/ash/ternary.pl Please use !! rather than :: at /Users/ash/ternary.pl:2 ------> my $abs = $var < 0 ?? -$var :⏏: $var; expecting any of: colon pair
63. Тип данных enum в Perl 6
В Perl 6 есть тип данных для создания перечислений — enum. Его использовать предельно просто:
enum colours <red yellow green>;
Так мы создали тип и три значения, которые теперь доступны как константы, но без сигилов:
say red; say green;
На печати появятся названия как они есть:
red green
Если необходимы числовые значения, используйте метод Int:
say red.Int; # 0 say yellow.Int; # 1 say green.Int; # 2
По умолчанию отсчет ведется с нуля, но это можно изменить, указав нужное значение первому элементу перечисления:
enum colours (red => 10, 'yellow', 'green'); say red; # red say green; # green say yellow; # yellow say red.Int; # 10 say yellow.Int; # 11 say green.Int; # 12
Обратите внимание, что Perl 6 понял, что первое значение — это пара red => 10, а дальше идут две строки, которые он не попытался объединить в пару yellow => 'green'.
Если необходимо, то можно сэкономить на кавычках, но при этом использовать чуть иные цитирующие кавычки:
enum colours << :red(10) yellow green >>;
Разумеется, можно присвоить свои значения каждому элементу:
enum colours (red => 10, yellow => 20, green => 30); say red.Int; # 10 say yellow.Int; # 20 say green.Int; # 30
62. Установка таймаутов в Perl 6
В Perl 5 таймауты устанавливали через сигналы (по крайней мере в моей практике, это был самый понятный способ). В Perl 6 для таймаутов можно воспользоваться промисами.
Давайте запустим бесконечный цикл и попытаемся завершить программу через две секунды.
Вот цикл. Время от времени он выводит счетчик на печать.
for 1 .. * { .say if $_ %% 100_000; }
Получив управление, этот цикл его никогда не отдаст. Поэтому, разумеется, таймер надо поставить до того, как мы дойдем до цикла:
Promise.in(2).then({ exit; }); for 1 .. * { .say if $_ %% 100_000; }
Метод Promise.in создает промис, который будет сдержан через заданное число секунд. На этот промис мы — с помощью метода then — добавляем другой промис, который будет сразу выполнен по окончанию первого. Тело промиса — вызов exit.
Запускаем и смотрим:
$ time perl6 timeout.pl 100000 200000 300000 . . . 3700000 3800000 3900000 real 0m2.196s user 0m2.120s sys 0m0.068s
Программа успела досчитать до четырех миллионов и завершилась через две секунды.
Для сравнения — программа на Perl 5:
use v5.10; alarm 2; $SIG{ALRM} = sub { exit; }; for (my $c = 1; ; $c++) { say $c unless $c % 1_000_000; }
(За две секунды она успевает досчитать до 40 миллионов на том же компьютере, но это другая история.)