Записи, связанные с тегом «5.10»

Мир до state

До прихода в Perl оператора state, который позволяет создавать «статические» переменные прямо внутри подпрограмм, люди пользовались вот таким приемом:

{
    my $count = 0;

    sub next_value {
        return ++$count;
    }

}

print next_value, "\n";
print next_value, "\n";


Теперь все ребята пишут так:

use v5.10;

sub next_value {
    state $count = 0;

    return ++$count;
}

say next_value;
say next_value;

Про /m

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

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

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

^ $ \A \Z \z

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

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

Интервью с Дамианом Конвеем

На сайте perl6.ru опубликован русский перевод интервью с Дамианом Конвеем, которое на днях появилось на сайте O’Reilly GMT.

Первая часть, в основном, посвящена Perl 6, но все, что написано дальше, я рекомендую прочитать всем, кто пишет и не пишет на перле. Вот выдержка, которая мне особенно понравилась:

«И — да, я часто слышу утверждение, что код, написанный на питоне (или яве, или на Эйфеле, или на любом другом языке, менее гибком, чем перл) по определению более читаем. Но это утверждение игнорирует фундаментальный факт о том, что синтаксис — лишь одно измерение, в котором люди выражают свою особенность и непоследовательность. И, во многих отношениях, наименее важное измерение.

Разумеется, весь код на питоне внешне должен выглядеть одинаково, но это лишь смещает внутреннюю сложность кода в какое-то другое измерение. Обычно это проявляется где-то в другом месте, в непоследовательном именовании переменных или методов, или в использовании трудных для понимания структур данных, или в странных API библиотек, или в пересечении функционального или процедурного стилей в том, как питон понимает объектное ориентирование.

В общем, плохие программисты будут программировать нечитаемо на любом языке. Перл лишь позволяет им делать это на самом простом и наиболее легком уровне — синтаксическом».

perl6.ru/interviews/damian-conway-on-perl-and-its-future

В операторе when — будь он внутри «родного» блока given или внутри цикла for — обычно происходит смартматчинг переменной $_ с указанным выражением. В документации perlsyn перечислены искючения, однако есть и не слишком очевидное поведение, о котором нужно знать.

Вначале о том, что явно указано как исключения. Блок when(EXPR) не выполняет смартматчинг $_ ~~ EXPR, а является просто булевым выражением, если EXPR — одно из следущего:

  • Вызов функции или метода.
  • Регулярное выражение (тут надо помнить, что операторы =~ и ~~ ведут себя по-разному).
  • Сравнение (как с переменной по умолчанию, так и с любой друой) или явный смартматчинг.
  • Отрицание (!, not или xor).
  • Файловый тест.
  • Операторы .. и ....

А теперь о том, где можно наступить на грабли. В предыдущей заметке встречались выражения типа

when(0b1110_0000 == ($_ & 0b1111_0000))

Дополнительная проверка на равенство здесь необходима (даже если проигнорировать младшие биты). Прямолинейная битовая операция

when($_ & 0b1111_0000)

сравнивает переменную $_ со значением, полученным после битовой операции $_ & 0b1111_0000, и для проверки установленных битов нужно выполнять явное сравнение, которое входит в один из пунктов перечисленных выше исключений.

Презентации с DevConf::Perl

В качестве бонуса — интервью с Карлом Мэсаком про Perl 6.

Perl 5.10 и 5.12 за 30 минут

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

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

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

Однострочники

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

for vs. given

Иногда вместо given удобно использовать for, например, чтобы сделать несколько проверок для каждого элемента массива.

Однако, эта легкость замены дает ложное ощущение возможности и обратного — использования given вместо for.

Есть такой старый трюк, когда for нужен лишь для того, чтобы топикализировать переменную и с легкостью пользоваться функциями, по умолчанию использующими $_.

Например:

for ($url) {
    s{\.?*}{};
    s{^/}{};
    s{/$}{};
}

Фактически никакого цикла здесь нет, и уж если подключен use 5.10, хочется использовать given. Однако, не тут-то было. Хотя given и создаст переменную $_, но она будет всего лишь лексической переменной в пределах блока given, а не синонимом $url.

use v5.10;
use strict;

my $string = 'Hello, Perl';
my $another = $string;

for ($string) {
    s/Hello/Hallo/;
    s/Perl/C++/;

}

given ($another) {
    s/Hello/Hi/;
    s/Perl/JavaScript/;

}

say $string;  # Hallo, C++
say $another; # Hello, Perl

Этот пример выведет измененную строку после блока с for и неизменную — после given. Будьте внимательны :-)

В серверном коде, обслуживащем сайт, блоки given/when весьма удобны в частности для того, чтобы последовательно протестировать URL запрошенного ресурса и выполнить соответствующие действия:

given($url) {
    when(m{^/$}) {home_page()}
    when(m{^/about/$} {about_page()}
    . . .
}

В Perl 5.10 помимо операторов given/when доступны именованные сохраняющие скобки, которые могут облегчить выделение параметров из адреса одновременно с его разбором:

given($url) {
    . . .
    when(m{^/news/(?<year>\d+)/(?<month>\d+)/(?<day>)/$})
        {news_that_day()}
}

Параметры, захваченные именованными скобками, попадают в хеши %+ и %-, которые следует передать дальше на обработку.

Однако, в зависимости от общей архитектуры (в нашем примере — схемы разбора URL) может проявиться ограниченность области видимости, в которой доступны упомянутые хеши. То есть сразу после закрывающей скобки, завершающей блок given, сохраненные значения перестают быть доступными.

given($url) {
    . . .
}
say Dumper(\%+); #
Пусто

В этом месте кода переменные %+ и %- сохраняют значения, полученные до начала блока given.

Вплоне логично, хорошо локализовано, но не всегда удобно.

Разумеется, возможно сохранить собранные данные непосредственно в блоке given, хотя при легкомысленном проектировании это легко приводит к большим фрагментам повторяющегося кода:

given($url) {
    . . .
    when(m{^/(?<type>this)_page/$})
        {%data = %+; do_this()}
    when(m{^/(?<type>that)_page/$})
        {%data = %+; do_that()}
}

Когда именованные сохраняющие скобки встречаются в регулярных выражениях вместе с обычными, может возникнуть вопрос о том, как перл будет эти скобки нумеровать.

use v5.10;
use strict;

my $string = "Fri Mar 19 13:59:06 MSK 2010";
$string =~ /(?<tz>([A-Z]{3}))/;
say $+{tz};
say $1;
say $2;

Программа трижды напечатает MSK.

Итого, ответ на вопроса простой: учитываются все скобки.

when со скаляром

Простейший вариант использования конструкции с ключевым словом when — указание констант в условиях проверки.

foreach (@hazards) {
    when ($WUMPUS) {
        $self -> lose;
        push @messages => "Oops! Bumped into a Wumpus!";
    }
    when ($PIT) {
        $self -> lose;
        push @messages => "YYYIIIIEEEE! Fell in a pit!";
    }
    when ($BAT) {
        push @messages => 

           "ZAP! Super bat snatch! Elsewhereville for you!";
    }
}

Games::Wumpus — 24 Nov 2009
Play Hunt the Wumpus

Забегая вперед, обратите внимение на то, что when не обязательно использовать исключительно внутри блока given. Любой вызов when выполняет сопоставление с переменной $_, поэтому они хорошо работают в конструкциях for и foreach, которые используют ее в качестве переменной по умолчанию на текущей итерации.

when для выбора вариантов

Выбор одного из нескольких вариантов — самое очевидное применениее конструкции given/when.

given ($k) {
    when ('file')    { $opt_file     = $v; }
    when ('argv')    { $opt_argv     = $v; }
    when ('inter')   { $opt_interact = $v; }
    when ('prompt')  { $opt_prompt   = $v; }
    when ('quiet')   { $opt_quiet    = $v; }
    when ('tty_in')  { $tty_in       = $v; }
    when ('tty_out') { $tty_out      = $v; }
    default {
        die "Error: in subroutine set_opt(), 
             found invalid key {$k => '$v'}
             (not 'file', 'argv', 'inter', 'prompt',
             'quiet',
 'tty_in' or 'tty_out')";
    }
}

Term::DBPrompt — 18 Dec 2009
Commandline prompt for a database application

given ($inp_typ)
    when ('f') . . .
    when ('a') . . .
    when ('i') . . .
    default {
        die "Internal error: type = 
             '$inp_typ' (not 'f', 'a' or 'i')";
    }
}

Term::DBPrompt — 18 Dec 2009
Commandline prompt for a database application

when с булевым выражением

Следующая «ступень» — использовать внутри when не константы, а выражения с переменной $_, в частности, булевые:

unless ( 'itan' ~~ @list ) {
    given ( length $password ) {
        when ( 16 ) {
            # ok
        }
        when ( $_ < 4 ) {
            die('ERROR: Password is too short 
                (Min 4 bytes required)');
        }
        when ( $_ > 16 ) {
            die('ERROR: Password is too long 
                (Max 16 bytes allowed)');
        }
        default {
            while (1) {
                $password .= '0';
                last 
                    if length $password == 16;
            }
        }
    }

App::iTan::Utils — 26 Oct 2009
Secure management of iTans for online banking

Кстати, в этом примере конструкция выбора обрамлена условием с использованием смартматчинга:

unless ( 'itan' ~~ @list )

when с регулярным выражением

Сделать проверку с использованием регуляного выражения так же просто, как и с константой.

sub range2list {
    my $_ = shift;
    given ($_) {
        when (/^(\d)\-(\d)$/o )   { return "$1..$2" }
        when (/^\d\.\.\d$/o )     { return "$_" }
        when (/^\d$/o )           { return $_}
        when (/^(.*?),(.*)$/o )   { return range2list($1). ','
                                          .range2list($2)}
        default                   { return ''}
    }
}

Catalyst::Devel

when и ref

Помимо проверки значения возможно проверять и тип переменной:

given(ref $fdef){
    when('ARRAY'){

Package::FromData — 14 Jan 2008
generate a package with methods and variables from a data structure

when и undef

Не столь очевидно, однако вполне законно, сопоставление с undef. В этом случае блок when принимает управление, если переменная неопределена.

given ($1) {
    when (undef)  {return}
    when ($left)  { $depth++; }
    when ($right) { $depth--; }
}

Parse::Marpa::Lex

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

given ($action) {
    when (undef) {;}    # do nothing
                        # Right now do nothing 
                        # but find lex_q_quote
    when ('lex_q_quote') {
        $lexers[$ix] =
            [ \&Parse::Marpa::Lex::lex_q_quote,
               $prefix, $suffix ];
    }

Parse::Marpa::Recognizer

Вложенные блоки given/when

Блоки given/when легко объединяются и образуют вложенные конструкции.

given($name) {
    when ('stream:stream') . . .
    when ('challenge') . . .
    when ('failure') . . .
    when ('stream:features') . . . 
        given(my $clist = $node->getChildrenHash()) {
            when ('starttls') . . .
            when('mechanisms') . . .
                foreach($clist->{'mechanisms'}->

                  [0]->getChildrenByTagName('*'))
                    when($_->textContent() eq 'DIGEST-MD5' 
                      or $_->textContent() eq 'PLAIN')
            when('bind') . . .
            default . . .
    when ('proceed') . . .
    when ('success') . . .

POE::Component::Jabber — 22 Mar 2009
A POE Component for communicating over Jabber

Стоит отметить, что вложенные конструкции в некоторых случаях возможно развернуть в одноуровневые.

for и when

Несмотря на то, что ключевое слово when появилось в Perl 5.10 одновременно с given и default, ничто не обязывает всегда использовать их совместно. Как уже упоминалось, действие, выполняемое функцией when, во многих случах явлется сопоставлением переменной по умолчания $_ с указанным значением (константой, регулярным выражением, списком и т. д.). Поэтому иногда when удобно применять вместо последовательности if/elsif/else.

for ( catch ) {
  when ( $_->isa('Getopt::Lucid::Exception::ARGV') ) {
    say;
    # usage stuff
    return 1;
  }
  default { die $_ }
}

App::CPAN::Mini::Visit — 07 Nov 2008
explore each distribution in a minicpan repository

Cмартматчинг (~~)

Оператор сопоставления используется довольно часто, хотя и менее популярен, чем оператор //.

Встречаются самые разные комбинации типов операндов, в том числе и сопоставление с регулярными выражениями.

return _fail( $pkg, $sub ) if $_ ~~ 0;

if ( $attr ~~ /^Export_?Lexical$/i ) {

Export::Lexical — 09 Oct 2008
Lexically scoped subroutine imports

Интересен пример использования оператора ~~ внутри блока кода встроенной функции grep.

@exportz = grep { ! ( $_ ~~ @argz ) } @_;

$disp ~~ @exportz or push @exportz, $disp;

Exporter::Proxy — 29 Jan 2010
Simplified symbol export & proxy dispatch

В этом же фрагменте есть еще не менее интересное и практичное применение: ~~ удобно привлекать, чтобы определить, содержится ли элемент с неким значением в списке.

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

push @exportz, $disp unless $disp ~~ @exportz;

Смартматчинг, выполненный внутри if, разумеется, возможен, однако в таких случаях стоит задумться об использовании when, поскольку он неявно использует именно смартматчинг.

for( @_ )
    {
        index $_, ':'
        or next;

        if( $_ ~~ @exportz )
        {
            my $source  = qualify_to_ref $_, $source;
            my $install = qualify_to_ref $_, $caller;

            *$install   = *$source;
        }
        else
        {
            die "Bogus $source: '$_' not exported";
        }
    }

Exporter::Proxy — 29 Jan 2010
Simplified symbol export & proxy dispatch

Именованные сохраняющие скобки

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

my $compiled_regex = qr{
    \G
    (?<mArPa_prefix>$prefix)
    (?<mArPa_match>$regex)
    (?<mArPa_suffix>$suffix)
}xms;

Parse::Marpa::Recognizer


Продолжение будет.

Часть III
Как пишут другие

В этом разделе собраны и сгруппированы примеры использования новых возможностей Perl 5.10, которые встречаются в модулях на CPAN.

Как включить режим Perl 5.10

Поскольку новые ключевые слова могут сделать программу несовместимой с предыдущими версиями компилятора, необходимо дать явные указания, чтобы активировать соответствующие фичи Perl 5.10.

Cпособы, которыми пользуются авторы, как нельзя лучше демоснтрируют принцип TIMTOWTDI:

use 5.010000;
use 5.01001;
use 5.010;
use 5.010_000;
use 5.10.0;
use v5.10.0;
use v5.10;
use feature ':5.10';

Строки, начинающиеся с буквы v, либо содержащие две точки, называются v-string (version string или, иногда, vector string). При объявлении версии интерпретатора следущие варианты эквивалентны.

5.10.0
v5.10.0
v5.10

Обратите внимание, если при указании версии используется обычное число (с одной десятичной точкой), то следует писать не 5.10, а 5.010.

say для отладки

Функцию say удобно использовать во время отладки программы для вывода промежуточных значений переменных. На CPAN можно найти следы такой отладки — закомментированный вызов say.

given ($action) {
    when (/^include_cmd:/) {
        my $cmd = $child->content;
        $cmd =~ /^include_cmd:(\s*)/;
        my $ws = $1 || '';
        $cmd =~ s/^include_cmd:\s*//;
        #say("cmd:$ws$cmd");
        $cmd = cwd() . '/' . $cmd;
        @output = qx($cmd);
        $child->content($ws . join($ws, @output));
    }

Pod::Elemental::Transformer::Include — 08 Jan 2010
include output via files and commands

(Здесь и далее рядом с примером кода указано название модуля, откуда взят пример, дата выхода его первой версии и авторское описание; пунктуация, отступы и пробелы в коде в большинстве случаев сохранены.)

// и //= для значений по умолчанию

Пожалуй, самое распространенное применение оператора defined-or — установка значений по умолчанию.

В частности, удобно пользоваться вариантом с присвоением.

$port //=  5432;
$host //= 'localhost';

$col  //= '';

Pg::Loader — 07 Jul 2008
Perl extension for loading Postgres tables

$attrz{ maxjob  } //= 1;
$value //= 1;

$attrz{ $_ } //= 0;
$attrz{ verbose } //= '';
$attrz{ debug   } //= '';
$val //= 1;
$exit //= 0;
$skipz->{ $job_id } //= 'Skip on SIGHUP'

Parallel::Depend — 12 Aug 2009
Parallel-dependent dispatch of perl or shell code

Чуть более сложный пример — с обращением к встроенной функции:

sub import   {
    shift;
    my %args = @_;
    # we do not care about autoviv
    $^H{fixedtime} = $args{epoch_offset} // 
                     CORE::time;
}

fixedtime — 14 Aug 2008
lexical pragma to fix the epoch offset for time related functions

Defined-or используют и непосредственно при передаче аргументов функциям.

say $answer // 
    "I don't know enough to answer you yet.";

Hailo — 29 Jan 2010
A pluggable Markov engine analogous to MegaHAL

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

my $marpa_version  = 
$Parse::Marpa::VERSION // 'undef';
my $source_version = 
$Parse::Marpa::Source::VERSION // 'undef';

$options //= {};

Parse::Marpa — 14 Dec 2008
Generate Parsers from any BNF grammar

В следующем примере помимо многократного использования // интересно отметить, что say передается файловый дескриптор.

my $nulling_symbol =
  $rhs_symbol->[Parse::Marpa::Internal::Symbol::NULL_ALIAS] // $rhs_symbol;

$action //= $default_action;

say {$trace_fh}
    'Problems compiling action for original rule: ',
    Parse::Marpa::brief_original_rule($rule);

my $clone = $clone_arg // 1;
my $current_parse_set = $parse_set_arg // $default_parse_set;

$choice //= 0;

$lines //= [0];
$source_options //= {};

Parse::Marpa::Internal::Evaluator — 14 Dec 2008
Generate Parsers from any BNF grammar

Наконец, и сам файловый дескриптор удобно вписывается в работу с defined-or:

my $trace_fh = $arg_trace_fh // (*STDERR);

my $trace_fh = shift;
$trace_fh //= *STDERR;

Parse::Marpa::Recognizer — 14 Dec 2008
Generate Parsers from any BNF grammar

// внутри return

Оператор defined-or часто встречается внутри вызова return, опять же чтобы вернуть определенное значение, если оно не получено на предыдущих шагах.

return $self->_get_infection( $disease->id ) // 0;

my $val = $self->_get($key) // $default->{$key};


return @{ $self->_players // [] };

Games::Pandemic::City, Games::Pandemic::Config — 07 Sep 2009
Games::Risk — 18 Oct 2008

Иногда в одном из операндов // вызывают фукнции, которые могут привести к досрочному завершению программы или выходу из блока.

sub homedir {
  my ($self) = @_;
  require File::HomeDir;
  return File::HomeDir->my_home
    // croak 'File::HomeDir says you have no home
              directory';
}

App::RSS2Leafnode — 02 Feb 2010
post RSS feeds to newsgroups

А вот пример, когда операндом является целый блок кода do:

sub config_filename {
  my ($self) = @_;
  return $self->{'config_filename'} // do {
    require File::Spec;
    File::Spec->catfile ($self->homedir, '.rss2leafnode.conf');
  };
}

App::RSS2Leafnode — 02 Feb 2010
post RSS feeds to newsgroups

В частных случаях // действует лишь внутри части выражения, передаваемое return.

return 
isodate_to_rfc822($date // $self->{'now822'});

return URI::Title::title
    ({ url  => ($resp->request->uri // ''),
       data => $resp->decoded_content 
 (charset => 'none')});

App::RSS2Leafnode — 02 Feb 2010
post RSS feeds to newsgroups

return внутри //

Весьма распространена и противоположная конструкция, когда в одном из плеч // делают попытку возврата из подпрограммы.

my $b_time = $self->item_to_timet($b_item)
   // return $a_item;

my $a_time = $self->item_to_timet($a_item) 
   // return $b_item;;

my $str = $self->item_to_date($item) 
   // return;

App::RSS2Leafnode — 02 Feb 2010
post RSS feeds to newsgroups

// легко объединяется в цепочку, поэтому возможно написать конструкцию, которая делает несколько попыток присвоить defined-значение:

return (elt_to_email ($item->first_child('author'))
        // elt_to_email ($item   ->first_child('dc:creator'))
        // elt_to_email ($item   ->first_child('dc:contributor'))
        // non_empty ($item->first_child_text('wiki:username'))

        // elt_to_email ($channel->first_child('dc:creator'))
        // elt_to_email ($channel->first_child('author'))
        // elt_to_email ($channel->first_child('managingEditor'))
        // elt_to_email ($channel->first_child('webMaster'))

        // elt_to_email ($item   ->first_child('dc:publisher'))
        // elt_to_email ($channel->first_child('dc:publisher'))

        // non_empty ($channel->first_child_text('title'))

        # RFC822
        // 'nobody@'.$self->uri_to_host
       );

App::RSS2Leafnode — 02 Feb 2010
post RSS feeds to newsgroups

Возможен и подход, при котором возвращается одна-единственная переменная, которая перед этим проходит через несколько проверок с оператором defined-or:

sub item_to_language {
  my ($self, $item) = @_;
  my $content;
  my $ret = (elt_to_language($item)
             // elt_to_language($item->first_child('content')));
  for (;;) {
    $item = $item->parent // last;
    $ret //= elt_to_language($item);
  }
  $ret //= $self->{'resp'}->content_language;
  return $ret;
}

App::RSS2Leafnode — 02 Feb 2010
post RSS feeds to newsgroups

Несколько //

Объединение в цепочку нам уже встречалось, однако на нем следует задержать внимание еще раз.

my $captures    = $arg {captures}       // [];
my $comment     = escape $arg {comment} // $name // "";
my $upgrade     = $arg {utf8_upgrade}   // 1;
my $downgrade   = $arg {utf8_downgrade} // 1;
my $match       = $arg {match}          // 1;

Games::Wumpus — 24 Nov 2009
Play Hunt the Wumpus


Продолжение будет.

Perl 5.10 в 2010-м — части I и II

Часть I
История и статистика

Perl 5.10 увидел свет в 20 день рождения языка — 18 декабря 2007 года. Уже прошло более двух лет, и за это время успели появиться девелоперские версии 5.11.0, 5.11.1, 5.11.2, 5.11.3 и 5.11.4, и вот-вот появится версия 5.12, предназначенная для использования в реальных приложениях.

Perl 5.10 представил много нововведений, и сегодня интересно посмотреть, как часто они используются авторами модулей CPAN — модулей, появившихся после релиза 5.10, либо обновленных с того времени.

На CPAN сейчас хранится около 80 000 модулей в 20 000 дистрибутивах, список авторов содержит около 8000 имен. Как часто здесь используются фичи Perl 5.10?

Ответ: новые фичи встречаются в двух сотнях модулей, созданных сотней авторов.


Часть II
Фичи Perl 5.10

Кратко о том, что появилось в Perl 5.10.

— Встроенная функция say, которая работает аналогично print, но добавляет перевод строки.

— Оператор сопоставления (смартматчинга) ~~ сопоставляет свои аргументы согласно здравому смыслу (и, разумеется, четким правилам, описанным в документации). Например, в этих примерах аргументы имеют разный тип, но используется один и тот же оператор.

$a ~~ /\d/

$a ~~ @list

@list ~~ %hash

— Составной оператор выбора под условным названием switch. С помощью новых ключевых слов given, when и default возможно создать условную конструкцию, аналогичную switch/case в C и других языках. Важное отличие в том, что внутри given происходит не простое сравнение, а сопоставление (или смартматчинг).

given($x) {
    when(/a/) {...}
    when('b') {...}
    default   {...}
}

— Модификатор state позволяет создавать лексические переменные, сохраняющие значение между вызовами подпрограммы. Использование state похоже на объявление автоматической переменной со словом static в C.

sub counter {
    state $c = 0;
    return ++$c;
}

— Новшества в регулярных выражениях. Регулярные выражения существенно переработаны, в частности, добавлены именованные сохраняющие скобки. В этой статье про регулярные выражение почти не упоминается, поскольку они достойны отдельного рассказа.

(?<name>)
\K
\R
%+
%-
\g<name>

— Бинарный оператор defined-or (//). Возвращает первый из аргументов, который содержит определенное (не undef) значение. Может использоваться и в варианте с присвоением.

Следующие два примера иллюстрируют логику, которая часто прослеживается в сообщениях о вакансиях.

$city = $arg // 'Moscow';

$vacancy{city} //= 'Moscow';


Продолжение будет.

5, 5.10, 5.10.1, 5.12 и 6 в Голландии

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

Perl 5.10 в 2010-м

Моя презентация на «Перлбурге».

Презентация в пяти частях, основное содержание — о том, как и зачем используют возможности 5.10 в новых модулях на CPAN и мои примеры вариантов применения, которые я показывал на onperl.ru.


View more presentations from andy.sh.

Как включить 5.10

На спане есть некоторое число модулей, которые используют новые возможности версии Perl 5.10.

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

use 5.010000;
use 5.01001;
use 5.0100;
use 5.010;
use 5.010_000;
use 5.10.0;
use v5.10.0;
use v5.10;
use feature ':5.10';

О том, что еще творится на спане, я расскажу на Перлбурге :-)

use everywhere

Те, кто активно пользуются версией Perl 5.10, каждый раз пишут в начале файла инструкцию use v5.10 (в разных вариантах) или use Modern::Perl.

Брок Вилкокс создал (еще в августе 2009-го) модуль-прагму everywhere, и в документации прямо пишет о том, что сделал это для того, чтобы не писать надоедливые use 5.010.

Если вставить инструкцию everywhere до подключения других модулей, то ее аргумент автоматически превращается в соответсвующий вызов use внутри подключенных файлов.

use everywhere q(feature ':5.10');
use MyModule;

MyModule->my_sub($$);

Соответственно, внутри MyModule.pm теперь возможно использовать фичи 5.10:

package MyModule;

sub my_sub {
    say $_[1];
}

1;

Помимо 5.10 удается подключать и любые другие модули и прагмы. В документации, к примеру, упомянут вариант

use everywhere 'MooseX::Declare';

При попытке написать

use everywhere 'v5.10';

или

use everywhere '5.10.1';

появляется предупреждение о том, что v-string in use/require non-portable. Это удается обойти, написав переносимую (хотя и менее понятную человеку) конструкцию

use everywhere '5.010';

В качестве шутки можно избавиться от предупреждения и таким образом:

use v5.10;
use everywhere 'v5.10';

for и when

То, что описано в этой заметке, вполне можно было бы записать в список каверзных задач на собеседовании.

Простой код, который подсчитывает четные числа в заданном списке.

use v5.10;

my @array = (1..20);
my $count = 0;
for(@array) {
    when(/[02468]$/) {$count++}
    say $_; # либо просто say;
}

say $count;

Вопрос: что будет напечатано на экране?

С последней строкой все понятно — она напечатает счетчик $count, и в нем содержится правильный ответ 10. Но вот say $_ внутри цикла напечатает лишь все нечетные числа от 1 по 19.

Блок кода, выполняемый функцией when(), содержит неявный оператор next, который не дает возможности продолжить выполнять тело цикла после первого удачного сопоставления. (Об этом сказано в документации.)

Продолжить цикл возможно стандартным образом, дописав continue:

for(@array) {
    when(/[02468]$/) {$count++; continue;}
    say $_;
}

В качестве дополнительного задания можно спросить, а что изменится, если в условии вместо регулярного выражения записать when($_ % 2).

А затем так: when(!($_ % 2)).

state (2)

Хотя вчера я и написал, что ключевое слово state в перле похоже на static в C++, документация утверждает, что это не вполне так. Описано, впрочем довольно размыто.

Что еще нужно знать про state, кроме того, что эта инструкция стала доступна только с версии Perl 5.10?

Во-первых, state можно эмулировать и в ранних версиях.

{
    my $counter;
    sub counter {
        return ++$counter;
    }
}

print counter(), "\n"; # 1
print counter(), "\n"; # 2

Переменная $counter, оказываясь в замыкании, с одной стороны, недоступна для внешнего кода, а с другой, сохраняет свое значение между вызовами функции counter().

Во-вторых, для переменной, объявленной с ключевым словом state, возможно возвращать обычную ссылку, что дает возможность изменять значение переменной.

use v5.10;

sub counter {
    state $counter;
    ++$counter;
    return \$counter;
}

my $c = counter();
say ++$$c;        # 2
say ${counter()}; # 3

В-третьих, state-переменную допустимо инициализировать при создании, но это разрешено только для скаляров.

use v5.10;

sub counter {
    state $counter = 10;
    return ++$counter;
}

say counter(); # 11
say counter(); # 12

Попытка же инициализации списка или хеша приводит к ошибке:

state @a = (1, 2, 3);
Initialization of state variables in list context currently forbidden

state @a //= (1, 2, 3);
Can't modify private array in defined or assignment (//=)

В документации (perlsub) явно указано, что такие присвоения в языке не определены.

Тем не менее, при необходимости обойти это ограничение довольно просто.

state @a;
@a = (1, 2, 3) unless defined @a;

state (1)

Из записок технического менеджера.

A

За последние несколько дней несколько раз рассказывал, что моя жена в свое время преподавала в университете C++ студентам-физикам. Сам-то я до сих пор испытываю нереализованное желание писать на этом замечательном языке — первом, в котором я досконально и по-взрослому разобрался.

В C++ есть модификатор static, который в применении к переменной означает, что ее значение не нужно забывать между вызовами функции.

Б

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

В Perl 5.10 модификатор state делает то же самое, что и static в C++ — позволяет сохранять значение переменной между вызовами подпрограммы.

B

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

Сегодня как раз предоставился такой случай. Мне потребовалось выбрать случайную строку из большой базы данных. Разумеется, sort by rand() не подходит, а вот limit $N, 1 работает вполне быстро.

my $N = int rand $count;

Тут все понятно: чтобы узнать общее число записей $count, нужен еще один запрос (к тому же он с дополнительными ограничениями where). Вместо того, чтобы возиться с кешированием этой величины (memcached, BEGIN, etc.), достаточно поместить значение в статическую переменную, и вычислять ее, если она неопределена.

state $count;

unless ($count) {
    #
SQL-запрос select count(*)
}

my $offset = int rand $count;
#
SQL-запрос select ... limit $offset, 1

В файлик todo.txt заносится задача: использовать state совместно c defined-or (//).

Страницы

  • img

Теги

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