Последнее в категории Версии Perl

В первой части были приведены три программы, работающие под Perl 5.12, и предлагалось определить, что они выведут.

А выведут они следующее.

Программа А.

use v5.12;
my @letters = 'a'..'z';
for (my ($number, $char) = each @letters) {
    say "$number $char";
}

0 a
0 a

Программа Б.

use v5.12;
my @letters = 'a'..'z';
say $_ for each @letters;

0
a

Программа В.

use v5.12;
my @letters = 'a'..'z';
say $_ foreach @letters;

a
b
c
. . .
x
y
z

Серия вопросов для собеседования на понимание работы each из Perl 5.12 в применении к массивам (требовать заранее знать, что делает each @a, разумеется не обязательно, можно и объяснить, но вопросы остаются теми же).

Программа А.

use v5.12;
my @letters = 'a'..'z';
for (my ($number, $char) = each @letters) {
    say "$number $char";
}

Программа Б.

use v5.12;
my @letters = 'a'..'z';
say $_ for each @letters;

Программа В.

use v5.12;
my @letters = 'a'..'z';
say $_ foreach @letters;

Что эти программы напечатают и почему?

Британец Барри Уолш пишет в своем блоге о важной особенности работы оператора each в Perl 5.12.

Вызов each, примененный к массиву, возвращает пару величин: индекс и значение очередного элемента. Очевидно, что в пределах цикла (напрмер, while) each сохраняет указатель на текущую позицию.

use v5.12;

my @letters = 'a'..'z';
while (my ($number, $char) = each @letters) {
    say "$number $char";
}

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

use v5.12;

my @letters = 'a'..'z';
while (my ($number, $char) = each @letters) {
    say "$number $char";
    last if $number == 15;
}

say "Second loop";
while (my ($number, $char) = each @letters) {
    say "$number $char";
}

В этом примере напечатается следующее:

0 a
1 b
2 c
. . .
14 o
15 p
Second loop
16 q
17 r
. . .
24 y
25 z

Видно, что each продолжил с того же места, где остановился в первом цикле.

Сбросить счетчик удается вызовом keys @array или values @array. Также важно, что изменение массива (с помощью push или pop) и даже присвоение новых значений не изменяет позицию счетчика:

use v5.12;

my @letters = 'a'..'z';
while (my ($number, $char) = each @letters) {
    say "$number $char";
    last if $number == 15;
}

@letters = 'A'..'Z';
say "Second loop";
while (my ($number, $char) = each @letters) {
    say "$number $char";
}

В этом случае второй цикл упрямо продолжит с того же места, но уже с другим содержанием:

0 a
1 b
2 c
. . .
14 o
15 p
Second loop
16 Q
17 R
. . .
24 Y
25 Z

Про /m

| 1 комментарий

Бывает, что какой-то возможностью либо настолько редко пользуешься, либо она настолько неудобна, что про нее забываешь напрочь.

Сегодня меня заново познакомили (hsw++) с модификатором регулярных выражений /m.

В дизайне регулярных выражений Perl 5 есть аж пять метасимволов, совпадающие с границами строк (физических или логических):

^ $ \A \Z \z

И на все это наслаивается модификатор /m, изменяющий действие первых двух из списка. И, до кучи, пара символов для переноса строк (\n и \R). Полное безобразие. Иными словами, отказ от /m в Perl 6 — очень правильное решение.

Я погрепал свои старые файлы, и обнаружил, что этот модификатор, даже когда и используется, ставился или по инерции, или, в совсем старом коде, из-за трепетного отношения к магическим в то время регулярным выражениям.

На днях прочитал 42 статьи о том, как пользоваться юникодом в современном C++ и заодно придумал еще один практический пример, где может быть полезна state-переменная, появившаяся в Perl 5.10, — при разборе последовательности байтов UTF-8.

Вот простая функция parse_utf8, которая принимает очередной байт и формирует в переданном по ссылке массиве @$buf последовательность кодов символов. Важно, что функция при каждом вызове принимает байт, но выходной буфер изменяется только после того, как принята вся последовательность, соответствующая юникодному символу, то есть на каждый второй, третий или четвертый раз, если очередной символ требует для записи в UTF-8 несколько байтов.

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

use v5.12;

(5.12 удобно использовать потому, что инструкция use v5.12 автоматически подключает и use strict. Но все должно работать и с 5.10.)

sub parse_utf8 {
    my ($byte, $buf) = @_;
   
    state $bytes = 0;
    state $value = 0;
    my $mask = 0;
   
    given($byte) {
        when(!($_ & 0x80)) {
            ($bytes, $value, $mask) = (0, $byte, 0);
        }
        when(0b1111_0000 == ($_ & 0b1111_1000)) {
            ($bytes, $value, $mask) = (3, 0, 0b0000_0111);
        }
        when(0b1110_0000 == ($_ & 0b1111_0000)) {
            ($bytes, $value, $mask) = (2, 0, 0b0000_1111);
        }
        when(0b1100_0000 == ($_ & 0b1110_0000)) {
            ($bytes, $value, $mask) = (1, 0, 0b0001_1111);
        }
        when(0b1000_0000 == ($_ & 0b1100_0000)) {            
            $bytes--;
            $mask = 0b0011_1111;           
        }
        default {
            $mask = 0;
            $value = ord('?');         
        }
    }
   
    $value += ($byte & $mask) << ($bytes * 6) if $mask;
   
    push @$buf, $value unless $bytes;
}

Когда обнаруживается начало многобайтовой последовательности, в state-переменной сохраняется число оставшихся байтов, а в $value начинает накапливаться результат. Каждый последующий байт (старшие биты которого — единица и нуль) на единицу уменьшает значение $bytes.

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

Здесь есть даже примитивная обработка ошибок (хотя она находит ошибку лишь в первом байте многобайтовой последовательности).

Теперь проверяем:

my @buf;
parse_utf8($_, \@buf) for (
    0x34, # 4
    0x32, # 2
    0xd1, 0x9e, # ў
    0xc2, 0xa2, # ¢
    0xe3, 0x89, 0xbf, # ㉿
    0xe2, 0x82, 0xac, # €
    0xf0, 0xa4, 0xad, 0xa2, # 𤭢
);
say "&#$_;" for @buf;

Вызов say печатает HTML-сущности опознанных символов (да, строка "&#$_;" может вызвать улыбку):

&#52;
&#50;
&#1118;
&#162;
&#12927;
&#8364;
&#150370;
Именно такой результат и ожидался: 4 2 ў ¢ ㉿ € 𤭢.

В следующий раз обратим внимание на бинарные операции внутри when.

Сегодня на DevConf::Perl рассказал про то, как авторы модулей CPAN используют новые возможности, доступные в Perl 5.10, и о том, что нового появилось в 5.12. Кстати, как раз сегодня появился релиз Perl 5.12.1.

Идея собрать примеры использования со спана появилась спонтанно перед поездкой на Perlburg в начале этого года (хотя вначале я хотел всего лишь обновить свои прошлогодние «Фичи Perl 5.10 на практике», показав новые примеры своего кода).

Начиная с февраля я рассказал про Perl 5.10 на спане три раза, и, хотя каждый раз появлялось что-то новое (и почти полностью менялась аудитория), я немного устал от этого набора слайдов. Теперь хочу подготовить большой обзор существующего сегодня кода на Perl 6. Премьера 26 июня в Брюсселе.

use v5.12

| 1 комментарий

Onperl.ru, sayperl.org, moscow.pm.org, perl6.ru, yapc.tv — все эти сайты работают на Perl 5.12.

Server: Apache/2.2.10 (Unix) mod_apreq2-20090110/2.7.1 mod_perl/2.0.4 Perl/v5.12.0

В отличие от перехода с 5.8.8 на 5.10, нынешний апгрейд прошел вдвое быстрее.

Perl 5.12 и \w

| Комментариев: 2

Некоторое время назад в файле perl5110delta появился странный раздел о том, что метасимволы \w и \d в регулярных выражениях должны совпадать только с ASCII-символами:

digit   \d  [0-9]
word    \w  [0-9A-Z_a-z]

К счастью, это пока не перекочевало в Perl 5.12, причем независимо от того, определен ли при компиляции символ PERL_LEGACY_UNICODE_CHARCLASS_MAPPINGS или нет.

Программа, от которой ожидается совпадение \w с русскими буквами, по-прежнему работает:

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

"123abcабв" ~~ /(\w+)/;
say $1;

На печать выводится 123abcабв.

  • Брайн ди фой пишет новую редакцию книги Effective Perl Programming.
  • С новым маком и Mac OS X 10.6 в комплекте идет Perl 5.10.0.
  • Алекс Капранов анонсировал CPAN Hubble — одновременный поиск по CPAN и GitHub.

См. также Новое в Perl 5.12 — часть I

each для массивов

Встроенная функция each работает с массивами. Она возвращает пары значений «индекс — значение», каждый раз сдвигаясь по массиву на единицу:

use v5.12;

my @a = 1..10;
while (my ($a, $b) = each @a) {
    say "$a, $b";
}

Эта программа печатает следующее:

0, 1
1, 2
2, 3
3, 4
4, 5
5, 6
6, 7
7, 8
8, 9
9, 10

Постфиксная форма when

when теперь выглядит еще более независимым от given. В Perl 5.12 возможно использовать when в качестве модификаторов инструкций, как это разрешено для if и unless.

use v5.10;

for ('a'..'z', 'A'..'Z') {
    say when /[aeiou]/i;
}

Локальный delete local для хешей

Комбинация ключевых слов delete local позволяет локально удалить элемент из хеша. После выхода из соответствующей области видимости хеш остается прежним.

use v5.12;

my %h = (
    alpha => 'a',
    beta => 'b',
    gamma => 'g',
);

say for keys %h; say; # gamma beta alpha

{
    delete local $h{beta};
    say for keys %h; say; # gamma alpha
}

say for keys %h; say; # gamma beta alpha

Кроме того

  • Perl 5.12 включает обновленные таблицы юникода.
  • В функциях работы со временем решена проблема 2038 года.
  • Разрешена перегрузка оператора qr.
  • Находясь внутри when, // ведет себя как || (э-м-м, требует осмысления).

Наконец, есть некоторые изменения в работе смартматчинга (в том числе, и в given/when), которые появились еще в Perl 5.10.1 (часть 1, часть 2).

Страницы

  • img

Об архиве

Эта страница содержит последние записи категории Версии Perl.

Предыдущая категория — Веб.

Следующая категория — Видео.

Смотрите новые записи на главной странице или загляните в архив, где есть ссылки на все сообщения.