Выпуск 28. Июнь 2015

Рефакторинг Legacy | Содержание | Полный список изменений в Perl 5.22.0

Что нового в Perl 5.22

Краткий обзор наиболее заметных изменений в свежем стабильном релизе перла

Версия 5.22, в отличие от нескольких предыдущих, содержит некоторые интересные новшества, о которых стоит рассказать.

Регулярные выражения

Новый перл поставляется с новым юникодом версии 7.0. Одновременно появилось несколько выражений, которые можно использовать для поиска границ букв, слов и предложений:

  • \b{gcb} или \b{g} — границы букв (точнее, графем: grapheme cluster boundary);
  • \b{wb} — границы слов (word boundary);
  • \b{sb} — границы предложений (sentence boundary).

Пробуем делить по графемам (важно не забыть дописать инструкции про utf8).

use v5.22;
use utf8;
use open qw(:std :utf8);

my @str = ('Über', "U\x{308}ber");

for (@str) {
    my @ch = split //;
    say scalar @ch;
    say join ',', @ch;

    my @gl = split /\b{g}/;
    say scalar @gl;
    say join ',', @gl;
}

Ожидаемо, первый split // делит строку на байты (4 для первой строки и 5 для второй), а второй split /\b{gcb}/ на буквы.

Выражение \b{wb} совпадает на границе юникодных слов. Причем речь идет не только о составных символах (как на примере Ü выше). Например, слова с апострофом принимаются за слово целиком вместе с апострофом.

my $book = "Perl pour l'impatient";
say for grep /\S/, split /\b{wb}/, $book;

Этот пример напечатает три слова, а если разделить строку по шаблону /\b/, то пять слов (включая апостроф, выделенный в отдельное слово).

Наконец, выражение \b{sb} совпадает с границами предложений. Границей предложения могут стать и кавычки с цитатой, и точки после инициалов и даже перевод строки (точнее, все, что совпадает с /\R/). То есть реальной пользы от \b{sb}, видимо, не очень много.

my $text = <<TEXT;
Ultimately, it's Larry's choice. I haven't been able to convince Larry, he hasn't been able to convince me, but he has good reasons for the choices that he's made and he's a really smart guy. It might be that having Perl 5 and Perl 6 will actually turn out to be an advantage in some way that most of the rest of the community doesn't realize until we look back and say: “You know what? Larry was right again!”
TEXT

my @s = $text =~ /(.+?) *\b{sb}/g;
say "[$_]" for @s;

Подробные правила, принятые в юникоде для членения текста на значимые части, описаны в документе Unicode Text Segmentation.

/n для запрета захвата

В дополнение к (?:...) появился модификатор /n, который действует сразу на все выражение.

#'2015-06-02' =~ /(\d+)-(\d+)-(\d+)/; # захватит
'2015-06-02' =~ /(\d+)-(\d+)-(\d+)/n; # не захватит

say $1;
say $2;
say $3;

use re ‘strict’

В качестве экспериментальной добавлена возможность дополнительной проверки правильности регулярных выражений:

no warnings 'experimental::re_strict';
use re 'strict';

В документации [показан пример](http://search.cpan.org/~rjbs/perl-5.22.0/ext/re/re.pm#‘strict’_mode), когда такая проверка выдает предупреждение:

 qr/\xABC/

Без strict-режима все произойдет молча, а при наличии use re 'strict'; появится сообщение:

Use \x{...} for more than two hex characters in regex;

Более явные ошибки, тем не менее, удается отследить и в обычном режиме. Например, попытка использовать несуществующий режим для поиска границ \b{ww}:

'ww' is an unknown bound type in regex;

Многое, по видимому, упирается в обратную совместимость. Было бы неплохо в будущем включать строгий режим по умолчанию, как минимум вместе с директивой use v5.22;, как это сейчас происходит c обычным use strict; при указании, например, use v5.12;.

Флаги по умолчанию

Инструкция вида use re '/ax' автоматически подключает все указанные в ней модификаторы ко всем регулярным выражениям до конца текущей области видимости. Соответственно, no re '/ax' отключает перечисленные флаги.

use re '/i';

my @p = "ABC" =~ /([a-z])/g;
say for @p;

В этой программе к регулярному выражению добавится модификатор /i, поэтому программа напечатает три строки с прописными буквами.

Обратите внимание, что сразу записать use re '/g' невозможно:

Unknown regular expression flag "g" at regex-flags.pl line 3.

Впрочем, это невозможно и в записью вида (?i:...):

my @p = "ABC" =~ /(?ig:[a-z])/;

Здесь сразу на помощь приходит use re 'strict':

Useless (?g) - use /g modifier in regex;

Операторы

Битовые операторы

Экспериментальная фича bitwise активирует набор операторов |., &., ^., ~. и, соответственно, |.=, &.=, ^.=, ~.=, которые выполняют побитовые действия, рассматривая аргументы как строки.

use feature 'bitwise';
no warnings 'experimental::bitwise';

say 400 |  142; # 414
say 400 |. 142; # 542

say 40 |  12;   # 44
say 40 |. 12;   # 52

В примерах с | выполняется операция над числами: например, 40|12 это 0x28|0x0С или 0b00101000|0b00001100, что дает 0b00101100 = 44.

С новым оператором побитовые операции происходят посимвольно: ord('4')|ord('1') = 52|49 = 0b110100|0b110001 = 0b110101 = 53, то есть символ 5, а ord('0')|ord('2') = 48|50 = 0b110000|0b110010 = 0b110010 (символ 2).

В операторах с точкой наличие или отсутствие пробела после нее решается в пользу более длинного оператора, сравните:

say 40 | .12; # 40
say 40 |. 12; # 52
say 40 |.12;  # 52

Аналогично работают и другие операторы.

Строковое побитовое сложение наверное удобно использовать для совмещения двух ASCII-строк, в которых символы из одной встают на пробельные позиции в другой (правда, заодно происходит преобразование в нижний регистр):

say "Hello,      !" |. "       World"; # hello, world!

С юникодом такие фокусы не имеют смысла:

use utf8;   
say "Привет,    !" |. "        мир"; # пѠивеѢ, миѠ! 

Тем не менее, введение новых операторов — вполне в пределах картины мира перла, в которой часть операторов не полиморфна, а разделяется на строковые и числовые (ср. с операторами сравнения). Кстати, изначально предлагалась сделать новые операторы более последовательными, а именно, для строковых операций использовать строковые же названия band, bor, bxor и bnot.

<<>>

Двойной ромб (double diamond) — более безопасная версия оператора <>. Отличие проявляется, когда он используется для чтения из файлов, имена которых указаны в командной строке.

Если файлов нет, то чтение происходит из стандартного потока ввода, и никакой разницы нет:

my $str = <>;
say $str;

При чтении из STDIN второй вариант делает то же самое:

my $str = <<>>;
say $str;

Если же при запуске программы в командной строке были указаны аргументы, то оператор-ромб последовательно открывает все файлы и читает из них, причем если аргумент содержит специальные символы типа > или |, то они ведут себя так же как в функции open, вызванной с двумя аргументами:

open my $f, 'ls |'; 

Следующий пример печатает содержимое текущего каталога:

open my $f, 'ls |';
print while <$f>;

Если такой аргумент передать в командной строке и при этом использоавть традиционный ромб, то происходит ровно то же:

print while <>;

Запуск:

perl diamond3.pl  'ls |'

Двойной ромб в таких случаях открывает файл вариантом функции open с тремя аргументами, и особой интерпретации таких символов не происходит:

open my $f, '<', 'ls |';
print while <$f>;

Эта программа будет пытаться открыть файл с именем ls |. Аналогично, чтобы открыть этот файл из командной строки:

perl ddiamond3.pl 'ls |'

достаточно воспользоваться двойным ромбом:

print while <<>>;

Новая многосимвольная форма оператора, судя по всему, потребовалась только для обратной совместимости.

Прочее

Из интересных возможностей, которые здесь не упомянуты, я бы обратил внимание на следующее:

Подробности можно прочитать в переводе perldelta, опубликованном в этом номере журнала.

Андрей Шитов


Рефакторинг Legacy | Содержание | Полный список изменений в Perl 5.22.0
Нас уже 1393. Больше подписчиков — лучше выпуски!

Комментарии к статье