Как-то совсем давно я ничего не писал, думаю уже пора :). Не так давно прошла конференция BlackPerl 2011. Я думаю, что уже многие в курсе относительного этого события.
Я на конференцию ехал без доклада, просто с желанием познакомится с Perl-комьюнити, послушать других и пофотографировать сие действо. Но вдохновленный докладчиками, решил и сам соорудить небольшое выступление. Поскольку, конкретной темы у меня не было, я решил просто рассказать про интересные вещи в Perl. Ничего особенного, но возможно кто-то найдет для себя что-то новое.
Приступим.
Недостаток такого подхода в том, что caller будет нам возвращать "__ANON__" в качестве имени нашего метода. И stack trace будет нечитабельным.
Это проблему призван решить модуль "Sub::Name" , он позволяет давать имена анонимным функциям. Эти имена будут показываться в стеке вызовов, но их нельзя использовать для вызова функции/метода.
Наш пример приобретет вид:
Вы встречали такой код - "$sth->execute() or die;" , а задумывались как он может работать, если "execute" возвращает количество измененных рядков для не "select" запросов. Ведь если по условию WHERE при UPDATE мы не нашли ничего, то это ж не является ошибкой. Получается, что метод возвращает нулевое истинное значение.
И вот самый интересный вариант нулевого истинного значения:
Просто несколько нюансов при работе с функцией local
В Perl есть файловый хендлер ARGV, с которого мы можем вычитать файлы переданные скрипту в качестве аргументов командной строки. В примере происходит следующее:
1. Мы помещаем имя файла в массив @ARGV, который должен содержать переданные скрипту аргументы
2. Локализируем "$/" значением undef
3. Вычитываем файл в переменную
При этом, можно вычитать несколько файлов одним потоком:
Детали можно прочитать в "perldoc -f require"
Причина в том, что Perl на место пустого регекспа поставит последний удачно совпавший регексп.
export PERL5OPT='-MData::Dumper'
И затем всегда Data::Dumper будет доступен в ваших однострочниках:
perl -e 'print Dumper([1,2])'
В посте "Чем плох eval?!" были описаны проблемы перехвата исключений в Perl и я рекомендовал использовать модуль "Try::Tiny". Но с "Perl 5.14" необходимость в "Try::Tiny" практически отпала, это было описано в посте "Наконец-то исправлена обработка исключений в Perl 5.14"
Я на конференцию ехал без доклада, просто с желанием познакомится с Perl-комьюнити, послушать других и пофотографировать сие действо. Но вдохновленный докладчиками, решил и сам соорудить небольшое выступление. Поскольку, конкретной темы у меня не было, я решил просто рассказать про интересные вещи в Perl. Ничего особенного, но возможно кто-то найдет для себя что-то новое.
Приступим.
Приватные методы
В Perl возможно реализовать приватные методы и делается это следующим образом:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Private accessors/methods | |
# Помещаем ссылку на локальную анонимную функцию в локальную переменную | |
my $name = sub { | |
my $self = shift; | |
... | |
} | |
# Используем как обыкновенный метод, но доступен оно только внутри класса | |
$self->$name(); | |
$self->$name('viktor'); | |
Недостаток такого подхода в том, что caller будет нам возвращать "__ANON__" в качестве имени нашего метода. И stack trace будет нечитабельным.
Это проблему призван решить модуль "Sub::Name" , он позволяет давать имена анонимным функциям. Эти имена будут показываться в стеке вызовов, но их нельзя использовать для вызова функции/метода.
Наш пример приобретет вид:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Private accessors/methods with correct "caller" | |
use Sub::Name; | |
# Помещаем ссылку на локальную анонимную функцию в локальную переменную | |
# и присваиваем ей имя "name" | |
my $name = subname "name" => sub { | |
my $self = shift; | |
... | |
} | |
# Используем как обыкновенный метод, но доступен оно только внутри класса | |
$self->$name(); | |
$self->$name('viktor'); |
Истина или Ложь
Вы встречали такой код - "$sth->execute() or die;" , а задумывались как он может работать, если "execute" возвращает количество измененных рядков для не "select" запросов. Ведь если по условию WHERE при UPDATE мы не нашли ничего, то это ж не является ошибкой. Получается, что метод возвращает нулевое истинное значение.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
my $rows_affected = $dbh->do('UPDATE ... WHERE') or die; | |
my $rows_affected = $sth->execute(...) or die; # Для не "select" запросов. | |
# $rows_affected может содержать ноль, но быть истинным. | |
# Значит в Perl может быть истинное нулевое значение | |
# И вот несколько вариантов при использовании | |
# которых Perl не сыпет предупреждения | |
"0.0" # Десятичная нотация. Я обычно использую именно этот вариант | |
"0E0" # Экспоненциальный ноль (такой вариант использует DBI) | |
"+0" # Положительный ноль | |
" 0" # Пробел перед нулем |
И вот самый интересный вариант нулевого истинного значения:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# строка, которая в числовом контексте соответствуют | |
# нулю, а в логическом - истине | |
"0 but true" | |
perl -wE 'say "0 but true"+5' | |
#Получим 5 без никаких предупреждений | |
# Но стоит изменить хоть один символ в заветной строке | |
perl -wE 'say "0 but true!"+5' | |
'Argument "0 but true!" isn't numeric in addition (+) at -e line 1. | |
# И мы сразу получим предупреждение |
Локализируемся
Просто несколько нюансов при работе с функцией local
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#---------------------------------------------------------# | |
# Локализация пакетных переменных | |
our $l = 5; | |
{ | |
local $l = 4; | |
say $l | |
}; | |
say $l; | |
#4 | |
#5 | |
#---------------------------------------------------------# | |
# Локализация переменных с лексической областью видимости | |
my $l = 5; | |
{ | |
local $l = 4; | |
say $l | |
}; | |
say $l; | |
# В результате получим ошибку | |
#Can't localize lexical variable $l at -e line 3. | |
#---------------------------------------------------------# | |
# Но возможна локализация элементов локально | |
# объявленных массивов/хешей | |
my @l = (2,3,5); | |
{ | |
local $l[2] = 4; | |
say $l[2]; | |
}; | |
say $l[2]; | |
#4 | |
#5 | |
#---------------------------------------------------------# | |
# Может быть полезно, например, в такой ситуации | |
{ | |
local $h->{RaiseError}; # no RaiseError | |
... | |
} |
Однострочник для чтения файла
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$content = do {local(@ARGV, $/) = $f; <ARGV>} |
В Perl есть файловый хендлер ARGV, с которого мы можем вычитать файлы переданные скрипту в качестве аргументов командной строки. В примере происходит следующее:
1. Мы помещаем имя файла в массив @ARGV, который должен содержать переданные скрипту аргументы
2. Локализируем "$/" значением undef
3. Вычитываем файл в переменную
При этом, можно вычитать несколько файлов одним потоком:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
perl -e 'while (<ARGV>){ … }' f1 f2 f3 |
@INC
Многие знают, что массив @INC содержит список путей, которые Perl использует для поиска подключаемых модулей. Но кроме путей @INC может содержать хуки в виде ссылки на функцию, объект, массив. В прагме "use everywhere" используется данная возможность.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use everywhere 'MooseX::Declare', | |
matching => '^MyApp', | |
use_here => 0; |
Детали можно прочитать в "perldoc -f require"
Осторожно! Пустой регексп
Я думаю многих удивит результат выполнения такого вот кода:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if ( "test1" =~ /t1/ ) { | |
say "YES"; | |
} else { | |
say "NO" | |
} | |
if ( "test2" =~ // ) { | |
say "YES"; | |
} else { | |
say "NO" | |
} | |
#YES | |
#NO | |
# Второй регексп не заматчится!!! |
Причина в том, что Perl на место пустого регекспа поставит последний удачно совпавший регексп.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$text =~ m/$re/; | |
# Такая запись тоже является опасной, поскольку | |
# переменная $re может содержать пустую строку | |
# Лучше записывайте так | |
$text =~ m/(?:$re)/; |
Переменная по-умолчанию
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Опасный код!!! | |
my @ar = qw/h e l l o/; | |
foreach (@ar) { | |
do_something(); | |
say $_; | |
} | |
# Никогда не используйте переменную "$_" | |
# если в цикле вызывается какая-то внешняя функция. | |
# Если вдруг где-то выполнится код похожий на: | |
... | |
open (my $fh, '<', 'file'); | |
while(<$fh>) { | |
... | |
} | |
... | |
# то | |
# 1. "say $_;" - ничего хорошего не выдаст | |
# 2. Все элементы массива "@ar" будут испорчены | |
# поскольку "$_" является алиасом, а не копией |
Переменные окружения
Многие используют переменную окружения PERL5LIB для указания пути к перловым модулям. Но кроме этой переменной окружения есть еще PERL5OPT, позволяющая задать опции командной строки, которые передаются интерпретатору Perl при запуске. Например, можно подключить определенные модули:export PERL5OPT='-MData::Dumper'
И затем всегда Data::Dumper будет доступен в ваших однострочниках:
perl -e 'print Dumper([1,2])'
Try::Tiny
Этот момент я освещал уже в своем блоге. В посте "Чем плох eval?!" были описаны проблемы перехвата исключений в Perl и я рекомендовал использовать модуль "Try::Tiny". Но с "Perl 5.14" необходимость в "Try::Tiny" практически отпала, это было описано в посте "Наконец-то исправлена обработка исключений в Perl 5.14"
Материалы по конференции
5 коммент.:
Давать именам анонимным сабрутинам очень просто:
local *__NAME__ = 'subname'
s/NAME/ANON/
Я так понимаю, что это мы просто зададим имя для всех анонимных функций. Хотелось бы увидеть как Вы реализуете аналогичное такому:
use v5.10;
use Sub::Name;
my $name1 = subname name1 => sub {
say( (caller(0))[3] );
say( (caller(1))[3] );
say( (caller(2))[3] );
};
my $name2 = subname name2 => sub { $name1->() };
my $name3 = subname name3 => sub { $name2->() };
$name3->();
# Результат будет таким
#main::name1
#main::name2
#main::name3
По стеку вызовов с такой конструкцией ходить не получится, да.
Имя задается не для всех анонимных функций, а для текущей выполняемой (local же).
> Имя задается не для всех анонимных функций, а для текущей выполняемой (local же).
local позволяет нам лишь временно задать имя в пределах некого блока, но имя будет актуально для всех анонимных функций( если они не переопределят его уже сами ). По этой причине caller и не работает, как мы хотим.
Отправить комментарий
Не забудьте добавить себя в постоянные читатели и включить уведомления о новых комментариях, либо воспользуйтесь RSS каналом ;)