Часть 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
Продолжение будет.