Месяц: Январь 2018
23. Встроенные математические константы в Perl 6
Сегодня мы посмотрим на математические константы, доступные в Perl 6 без загрузки дополнительных модулей.
Во-первых, число пи в двух вариантах: ASCII- и юникодном:
say pi; # 3.14159265358979 say π;
Для каких-то применений, возможно, будет чуть более удобна константа, равная двум пи — tau или τ:
say tau; # 6.28318530717959 say τ;
Наконец, число e, то, которое про экспоненту и логарифмы:
say e; # 2.71828182845905 say 𝑒;
Во второй строке здесь стоит символ U+1D452.
Для обозначения бесконечности можно пользоваться либо Inf, либо ∞:
say (100 ** 100) / ∞; # 0
22. Ввод данных с консоли
В интерактивных программах требуется читать вводимые пользователем строки. В Perl 6 для этого есть специальная функция prompt.
Она останавливает программу и ожидает, пока кто-нибудь что-нибудь напишет. Затем эта строка возвращается функцией в программу:
my $str = prompt; say $str;
prompt с аргументом
Функция prompt принимает аргумент — строку, которая будет напечатана перед тем, как начнется ввод:
my $name = prompt('Как вас зовут? > '); say "Вас зовут $name.";
$*IN.get
Реализация функции prompt очень простая — она вызывает метод get на объекте $*IN, который по умолчанию привязан к стандартному потоку ввода STDIN. Поэтому вместо prompt можно написать более криптографичненько:
my $str = $*IN.get(); say $str;
get
Наконец, есть и просто отдельностоящая функция get, которая делает то же самое:
my $str = get(); say $str;
Самолет, указатель и стрелки
21. Определение длины строки в Perl 6
Perl 6 сразу и без обиняков считает исходный текст программы сохраненным в UTF-8. Да и вообще, поддержка юникода в Perl 6 очен радует. Сегодня посмотрим, как узнать длину строки.
Решение в лоб, знакомое по опыту Perl 5, не работает. На попытку вызвать функцию length компилятор выдает инструкцию воспользоваться новыми возможностями:
Undeclared routine: length used at line 1. Did you mean 'elems', 'chars', 'codes'?
elems, chars и codes
Метод elems относится скорее к массивам, чем к строкам. А вот с chars и codes давайте разберемся.
Метод символов в строке:
say 'hello'.chars; # 5 say 'café'.chars; # 4 say 'привет'.chars; # 6 say '嗨'.chars; # 1
Все отлично работает независимо от языка и наличия диакритических знаков.
Метод codes возвращает число кодовых позиций, необходимых для записи строки. Во многих случаях результат будет совпадать с тем, что возвращает chars. Если же юникодный символ собран из таких частей, которые невозможно воспроизвести в одиночном символе (например, буква плюс какой-то хитрый диакритический знак, который в известных языках не применяется с данной буквой), то Perl не сможет привести этот символ к каноническому виду и, соответственно, chars покажет 1, а codes — 2.
Длина в байтах
Если нужно определить длину строки в байтах, то просто вызвать метод, например, bytes, не получится. Хотя скорее всего вы имели в виду кодировку UTF-8, Perl 6 желает услышать это явно. Действительно, при разном кодировании одна и та же строка занимет разное число байтов. Вот как это делается:
say 'hello'.encode('UTF-8').bytes; # 5 say 'café'.encode('UTF-8').bytes; # 5 say 'привет'.encode('UTF-8').bytes; # 12 say '嗨'.encode('UTF-8').bytes; # 3
В UTF-16, например, число байт будет отличаться:
say 'hello'.encode('UTF-16').bytes; # 10 say 'café'.encode('UTF-16').bytes; # 8 say 'привет'.encode('UTF-16').bytes; # 12 say '嗨'.encode('UTF-16').bytes; # 2
Разумеется, при попытке посчитать байты в Latin-1, часть строк не сможет быть конвертирована:
$ perl6 -e'say "ю".encode("Latin-1")' Error encoding Latin-1 string: could not encode codepoint 1102 in block at -e line 1
20. Функция MAIN в Perl 6, часть 3
На сегодня осталось рассмотреть возможности для управления поведением именованных аргументов. Возможностей немного, но они есть.
Именованные параметры
Перед тем как начать — вопрос: что должна вывести вот такая программа, если ее запустить без каких-либо параметров?
sub MAIN(:$value) { say "value=$value" }
Ответ может показаться неожиданным:
$ perl6 main-6.pl Use of uninitialized value $value of type Any in string context. Methods .^name, .perl, .gist, or .say can be used to stringify it to something meaningful. in sub MAIN at main-6.pl line 1 value=
Вместо примера использования мы получили сообщение об ошибке, которое говорит о том, что функция MAIN была-таки вызвана. То есть именованные аргументы не являются обязательными.
При этом если указать аргумент, отсутствующий в сигнатуре, все работает как надо:
$ perl6 main-6.pl --key=42 Usage: main-6.pl [--value=<Any>]
Решение проблемы может быть очень легким: если вы действительно ожидаете параметр, укажите, что он должен быть определен (defined):
sub MAIN(Str:D :$value) { say "value=$value" }
Теперь появляется желанный пример использования:
$ perl6 main-6.pl Usage: main-6.pl [--value=<Str>]
Именованные и позиционные параметры
Для параметров, в том числе и для именованных, возможно указать значение по умолчанию. Например:
sub MAIN($value, :$key = 'default') { say "$key=$value" }
В этом случае программа, запущенная без именованного параметра, тоже покажет пример использования, а не ошибку:
$ perl6 main-7.pl Usage: main-7.pl [--key=<Any>] <value>
ОК, но теперь возникает другая проблема. Последняя программа ожидает, что именованный параметр передан до позиционного.
Так работает:
$ perl6 main-7.pl --key=answer 42 answer=42
А так нет:
$ perl6 main-7.pl 42 --key=answer Usage: main-7.pl [--key=<Any>] <value>
Чтобы заработало и во втором примере, нужно сообщить об этом через специальную динамическую переменную:
my %*SUB-MAIN-OPTS = :named-anywhere; sub MAIN($value, :$key = 'default') { say "$key=$value" }
Довольно многословно, но как есть. Теперь можно передавать именованные параметры в любом месте командной строки.
Обязательное =
Наконец, следует обратить внимание на то, что значения именованных параметров требуют знака равенства и отсутствие пробелов вокруг него. Запись в командной строке --key answer 42 будет расценена как три аргумента.
А если значение не указано, то оно будет истиной:
$ perl6 main-7.pl --key 42 True=42
Итого, нельзя сказать, что все мегаудобно, но во многих случаях разбор параметров командной строки получается довольно простым.
19. Функция MAIN в Perl 6, часть 2
Вчера мы рассмотрели основные свойства функции MAIN. Сегодня коснемся чуть более сложных деталей.
Начнем с простой программы, похожей на ту, что мы видели вчера:
say 1; sub MAIN() { say 'MAIN' } say 2;
Несмотря на то, что инструкции печати расположены и до, и после MAIN, обе они будут выполнены до нее:
$ perl6 main-2.pl 1 2 MAIN
Здесь нет ничего нового: в любой программе на перле можно создать функцию прямо по ходу дела, и она не повлияет на процесс выполнения.
Модификация @*ARGS
Код, расположенный за пределами MAIN, может изменять значения аргументов командной строки перед тем, как функция их получит. Например:
@*ARGS[0] = '10'; sub MAIN($x, $y) { say $x + $y }
При запуске этой программы $x всегда будет 10:
$ perl6 main-3.pl 3 4 14
Типизированные аргументы
Сигнатура функции позволяет накладывать дополнительные ограничения на тип принимаемых аргументов:
sub MAIN(Int) { say 'An integer' }
Попытка передать строку будет пресечена:
$ perl6 main-4.pl 43 An integer $ perl6 main-4.pl abc Usage: main-4.pl <Int>
Эту особенность можно использовать и в мультифункциях, хотя поскольку аргументы командной строки — строки, могут возникнуть конфузы, например, как в следующей программе:
multi sub MAIN(Int) { say 'An integer' } multi sub MAIN(Str) { say 'A string' }
Со строкой проблем нет, но целое число почему-то не будет принято:
$ perl6 main-5.pl abc A string $ perl6 main-5.pl 42 Ambiguous call to 'MAIN'; these signatures all match: :(Int $) :(Str $) in block <unit> at main-5.pl line 5
Окончание завтра.
18. Функция MAIN в Perl 6
Сегодня мы рассмотрим функцию MAIN. Известно, что в скриптовых языках такой функции обычно нет. В Perl 6 она есть, но не обязательна. Если же она есть, то она будет выполнена 🙂
sub MAIN() { say 'Hello, World!'; }
Эта программа вполне себе будет работать, но в данном случае обертывать инструкции в функцию особого смысла нет.
Давайте посмотрим, что произойдет, если добавить какие-то действия вне MAIN:
say 'Hi'; sub MAIN() { say 'Hello, World!'; }
Будут напечатаны обе строки:
$ ./perl6 MAIN.pl Hi Hello, World!
Аргументы MAIN
Самое интересное происходит, если этой функции указать аргументы. Начнем с простого:
sub MAIN($x) { say $x; }
Запускаем и видим, что вместо результата (какого?) программа печатает пример использования:
$ perl6 MAIN.pl Usage: MAIN.pl <x>
Программа ожидает аргумента в командной строке. Этот аргумент будет передан в переменную $x:
$ perl6 MAIN.pl 42 42
Если указать два параметра, то мы вновь получим сообщение об ошибке, поскольку Perl 6 будет проверять сигнатуру функции MAIN, которая требует только одного аргумента.
А дальше можно применить общие знания о том, как работают сигнатуры в Perl 6. Во-первых, функции могут принимать именованные аргументы. Во-вторых, различие сигнатур делает возможным мультифункции.
Пример с именованными параметрами:
sub MAIN(:$value, :$power) { say $value ** $power; }
Эта программа работает так:
$ perl6 MAIN.pl --value=42 --power=3 74088
Пробуем мультифункции:
multi sub MAIN($a) { say "[$a]"; } multi sub MAIN($a, $b) { say "[$a and $b]"; }
В зависимости от числа переданных в командной строке величин, будет вызван один из двух кандидатов.
$ perl6 MAIN.pl 42 [42] $ perl6 MAIN.pl 42 43 [42 and 43]
Интересно, что пример использования в этом случае показывает два варианта:
$ perl6 MAIN.pl Usage: MAIN.pl <a> MAIN.pl <a> <b>
Если сегодня комментаторы не допишут еще интересностей про эту функцию, то поговорим об этом завтра.
17. Модификаторы в регексах Perl 6 vs. Perl 5
Добрый вечер! Тема сегодняшнего поста — как правильно ставить модификаторы в регексах Perl 6, если вы знакомы с регулярными выражениями Perl 5.
В целом все просто: если раньше буквы типа i ставились после регулярного выражения, то теперь — перед:
Было:
say 'A' =~ /a/i; # 1
Стало:
say 'A' ~~ m:i/a/; # 「A」
Возможен и второй вариант, когда модификатор ставится внутрь:
В Perl 5 было:
say 'A' =~ /(?^i)a/; # 1
В Perl 6 стало:
say 'A' ~~ /:i a/; # 「A」
Во втором случае пробел перед буквой не изменяет регекс, поскольку по умолчанию они работают в режиме, который раньше требовалось устанавливать модификатором /x. В Perl 6 такой модификатор более не нужен.
При попытке воспользоваться старым синтаксисом компилятор сообщит об этом на этапе компиляции:
===SORRY!=== Error while compiling /Users/ash/re-5.pl Unsupported use of =~ to do pattern matching; in Perl 6 please use ~~ at /Users/ash/re-5.pl:3 ------> say 'A' =~⏏ /a/i;
16. Создание своих операторов в Perl 6, часть 3
В третьей части мы рассмотрим еще два вида операторов — circumfix и postcircumfix. Начнем с более сложного на вид названия.
Postcircumfix
С операторами такого типа вы все встречались: типичный пример — индексирование массива: @a[$i]. Сам по себе оператор — это пара скобок. Операнды (аргументы) оператора — массив и индекс.
Еще два примера — круглые скобки для вызова функции и угловые скобки для создания массива:
my @a = < a b c >;
Создадим свой:
sub postcircumfix:<¿ ?>(Str $question, Str $answer) { say "Q: $question"; say "A: $answer"; } "Hello"¿"World"?;
Эта программа напечатает следующее:
Q: Hello A: World
В качестве домашнего задания, создайте более осмысленный пример оператора этого типа.
Circumfix
Этот тип оператора еще менее похож на оператор, но тем не менее, с точки зрения языка, это полноправный оператор. Рассмотрим пример с теми же символами:
sub circumfix:<¿ ?>(Str $str) { $str.chars } say ¿'Hello, World!'?; # 13
Программа печатает длину строки. Вопросительные знаки стали двумя частями оператора, одна часть стоит до, вторая после операнда.