Выпуск 34. Декабрь 2015
← Управление модулями и прекомпиляция в Perl 6 | Содержание | Использование Rust из Perl →Perl 6-винегрет
Новогодний оливье — сборная солянка про разные интересные штуки в Perl 6
Юникод
Хранение и обработка символов внутри строк Perl 6 происходит в формате NFG (Normal Form Grapheme). Это название придумано еще в бытность Parrot, а с практической точки зрения достаточно знать, что из любого символа можно получить его NFC-, NFD-, NFKC- и NFKD-формы :-) Это разные канонические и декомпозированные представления символов в юникоде.
Подробности про разные формы представлений с интересными примерами опубликованы на сайте юникода, а история NFG — в блоге Джонатана.
Посмотреть разные формы помогут одноименные методы, которые можно вызывать на односимвольных строках:
say $s.NFC; # codepoint
say $s.NFD;
say $s.NFKC;
say $s.NFKD;
Полное каноническое название возвращает метод .uniname
:
say 'й'.uniname; # CYRILLIC SMALL LETTER SHORT I
У строк есть полезный метод .encode
, позволяющий получить представление в той или иной юникодной кодировке:
my $name = 'Перл';
say $name.encode('UTF-8');
# utf8:0x<d0 9f d0 b5 d1 80 d0 bb>
(Этот метод не поможет перекодировать CP1215 в КОИ-8.)
Для закрепления материала предлагаю посмотреть на вывод упомянутых методов на разных хитрых символах:
unidump('☭');
unidump('ы');
unidump('å');
unidump('é');
unidump('ϔ'); # один из немногих символов,
# для которого различаются
# все канонические представления
unidump('й');
unidump('²');
unidump('Æ');
sub unidump($s) {
say $s;
say $s.chars; # число графем
say $s.NFC; # code point
say $s.NFD;
say $s.NFKC;
say $s.NFKD;
say $s.uniname; # юникодное название буквы
say $s.NFD.list; # списком
say $s.encode('UTF-8').elems; # сколько байтов
say $s.encode('UTF-16').elems;
say $s.encode('UTF-8'); # в виде utf8:0x<...>
say '';
}
Формы NFKC и NFKD, в частности, могут преобразовать надстрочные и подстрочные индексы в обычные цифры:
say '2'.NFKD; # NFKD:0x<0032>
say '²'.NFKD; # NFKD:0x<0032>
С помощью функции unimatch
можно узнать, принадлежит ли символ к той или иной группе символов в юникоде. Например:
say unimatch('ф', 'Cyrillic'); # True
Но нужно быть осторожным, чтобы не разжигать:
say unimatch('ї', 'Cyrillic'); # True
say unimatch('ï', 'Cyrillic'); # False
Здесь были одинаково выглядящие, но разные символы. Их NFD соответственно 0x<0456 0308>
и 0x<0069 0308>
, а имена — CYRILLIC SMALL LETTER YI
и LATIN SMALL LETTER I WITH DIAERESIS
.
Юникодные свойства можно проверить и регулярными выражениями:
say 1 if 'э' ~~ /<:Cyrillic>/;
say 1 if 'э' ~~ /<:Ll>/; # Letter lowercase
Для создания юникодных строк можно напрямую воспользоваться конструктором класса Uni
:
say Uni.new(0x0439).Str; # й
say Uni.new(0xcf, 0x94).Str; # Ï
Whatever (*)
Whatever — это класс, который позволяет создавать объекты, тип и значение которых определяются из контекста. Чтобы создать такой объект, достаточно поставить звездочку (если она, разумеется, не означает умножение).
say *.WHAT; # (Whatever)
Whatever удобно использовать для создания простых функций, например:
my $f = * ** 2; # возведение в квадрат
say $f(16); # 256
my $xy = * ** *; # возведение в произвольную степень
say $xy(3, 4); # 81
Того же результата удастся достичь более традиционным (для старого Perl 6) синтаксисом:
# Анонимный блок кода с одним аргументом $x
my $g = -> $x {$x ** 2};
say $g(16);
# Блок с двумя аргументами, имена сортируются по алфавиту
my $h = {$^a ** $^b};
say $h(3, 4);
Вполне оправдано использование звездочки для создания интервалов (Range):
Пример с циклом:
for (5..*) {
.say; # построчно печатает от 5 до 10
last if $_ == 10;
}
Пример с массивом:
my @a = <2 4 6 8 10 12>;
say @a[3..*]; # (8 10 12)
Отдельно следует рассмотреть, что происходит с отрицательными значениями.
Все, начиная с четвертого элемента и заканчивая предпоследним:
say @a[3..*-2]; # (8 10)
Чтобы получить последний элемент, нельзя просто написать @a[-1]
:
Unsupported use of a negative -1 subscript to index from the end;
in Perl 6 please use a function such as *-1
Следует добавить звездочку:
say @a[*-1]; # 12
Звездочка подходит и для создания ленивых списков (то есть таких списков, очередной элемент которых вычисляется по мере необходимости):
my @a = (1 ... *);
for (0..5) {
say @a[$_];
}
В ленивых списках допустимо подсказать компилятору, как генерировать последовательность:
my @a = (1, 2 ... *); # с шагом 1
my @a = (2, 4 ... *); # четные числа
my @a = (2, 4, 8 ... *); # степени двойки
Либо использовать явно определенный блок со способом вычисления:
my @fib = 0, 1, * + * ... *; # Фибоначчи
Файлы
Имя файла, который сейчас исполняеся, теперь находится в переменной $*PROGRAM-NAME
, а не $*PROGRAM_NAME
, как было еще совсем недавно.
А вот программа, которая использует встроенную функцию slurp
, читающую содержимое текущего файла:
say slurp $*PROGRAM-NAME;
Противоположная функция — spurt
(«бить струей»), она записывает данные в файл. Вот пример реализации юниксовой функции cp
на Perl 6:
my ($source, $dest) = @*ARGS;
my $data = slurp $source;
spurt $dest, $data;
По умолчанию файл либо создается, либо перезаписывается. Ключ (именованный аргумент) :append
попросит добавлять новые данные в конец файла:
spurt $dest, $data, :append;
Чтобы упасть с ошибкой при попытке записи в существующий файл, добавьте опцию :createonly
(:append
при этом станет неактуальной):
spurt $dest, $data, :createonly;
И slurp
, и spurt
принимают аргумент с именем кодировки enc
, но опять же, старых кодировок типа 'KOI8'
там нет (да и не надо):
my $data = slurp $source, enc => 'UTF-8';
spurt $dest, $data, enc => 'UTF-16';
Если на строке вызвать метод .IO
, то возвращается объект класса IO::Path. Метод определен так:
method IO() returns IO::Path:D
(:D
здесь не ржачный смайл, а означает defined.)
Например, как получить абсолютный путь к локальному файлу:
say 'file1.pl'.IO.abspath;
Как получить содержимое такого-то каталога:
say '.'.IO.dir;
Проверки существования файлов и каталогов теперь выглядят так:
say 1 if 'file.txt'.IO.e; # -e 'file.txt' в Perl 5
say 1 if 'file.txt'.IO.f; # -f
say 1 if '..'.IO.d; # -d
Остальные методы, перечисленные в документации, вполне понятны без объяснений. Например, для выделения из имени файла расширения можно воспользоваться методом .extension
:
say $filename.IO.extension;
← Управление модулями и прекомпиляция в Perl 6 | Содержание | Использование Rust из Perl →