среда, 31 марта 2010 г.

Тест на понимание Perl: Вопрос 2

@a = (2,3,4,5); my $a = {a=>"q", b=>@a, c=>2}; print $a->{c}; Ответ: undef
Пояснение: хеш представляет собой список с пар(ключ/значение). значение(как и ключ) всегда представляет собой скаляр.  Оператор => - по своей сути, это синоним оператору ",", только он еще берет левый аргумент в двойные кавычки. 



Эта запись {a=>"q", b=>@a, c=>2} на самом деле
равна  {"a", "q", "b" , @a, "c", 2}, 
либо  {"a", "q", "b" , 2, 3, 4, 5, "c", 2} 
либо
{a => "q", b =>  2, 3 => 4, 5 => "c", 2 }  и можем видеть, что ключа "c" просто нет в хеше.

Если мы хотим, обращаться по ключу к массиву, то мы должны указать в качестве значения ссылку на масив, а не масив как список.
my $a = {a=>"q", b=>\@a, c=>2}.

понедельник, 29 марта 2010 г.

Тест на понимание Perl: идея и вопрос 1

Идея
Решил написать серию постов с вопросами на понимание  Perl. То есть каждый пост - отдельный вопрос.  Каждый такой пост будет содержать: вопрос, ответ, объяснение ответа.


Вопросы не будут содержать каких-либо редко используемых функций. Главное здесь проверить не знание функций, а понимание поведения Perl-кода.  Возможно многим они покажутся очевидными, но это говорит о том, что вы понимаете Perl ;)




Когда вопросов насобирается 20 штук - соберу все в один  тест. 

Вопрос 1
@a = 0; print 10 if @a 

Ответ: 10

Пояснение:  Все логические операции работают в скалярном контексте. В данном случае запись эквивалентна print 10 if scalar(@a)




суббота, 27 марта 2010 г.

Комментарии на русском языке(utf-8) в perl коде - ЗЛО || ДОБРО ?

Какие могут быть проблемы? Я пока вижу следующие:
Проблема 1. Необходимо всем перенастроить редакторы кода.  У нас в компании нет ограничения на использование редакторов кода - используются Vim, Eclipse, Geany, Far... С Far есть явная проблема, которая заключается в необходимости постоянного переключении редактора в режим юникода для просмотра комментария. С vim тоже есть проблема - при переключении раскладки на русский язык не работают хоткеи(конечно можно помучится и настроить их), что реальное неудобно. Я думаю, что программисты, которые пишут в vim просто будут писать комменты на английском. По поводу Geany я ничего сказать не могу, с Eclipse должно быть все хорошо.

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

Проблема 3. Post commit SVN хук, который отправляет diff на почту разработчикам не дружит с не ASCII символами. Допускаю(но не утверждаю :) ), что возможно будут проблемы с svn diff и слиянием веток.

Проблема 4.. Непоследовательность. Разработка ведется на Perl и на Javascript, но для Javascript нельзя точно нельзя разрешать кириллицу, поскольку это может вылезти серьезными багами(проверено на собственном опыте). Только часть(10-30%) разработчиков будет использовать кириллицу.

Проблема 5(потенциальная).. Сразу ограничиваем поддержку кода, только русскоговорящим населением.

Возможно имеет смысл создавать отдельные ru/SomeModule.pod файлы для кириллической документации.

Возможно есть еще какие-то  проблемы и подводные камни? Буду рад увидеть в комментариях мнения по этому поводу. Возможно кто-то уже использует  кириллицу и готов поделится опытом?

ЗЫ: я не люблю читать модули в CPAN с документацией на японском ;)

воскресенье, 21 марта 2010 г.

Экономия памяти: прием по ссылке

Не для кого не секрет, что Perl любит кушать память и при написании кода нужно обращать внимание на всякие мелочи, если вы работаете с большими объемами данных. Проблема заключается в том, что когда Perl съедает память, он ее не возвращает системе, а оставляет себе для повторного использования. Для долгоживущих процессов это иногда создает проблемы.

Допустим у нас есть огромный блок данных - $data;
и функция для записи данных в файл $file - set_file_content;
sub set_file_content {
    my ($file, $data) = @_;
    ....
   print FILE $data;
}

Вариант 1
set_file_content("$file", "$data") - самый нерациональный вариант вызова. Параметры заключены в двойные кавычки и Perl создаст в памяти еще одну копию строки для передачи в функцию.

Вариант 2
set_file_content($file, $data) - хороший вариант вызова, но тут уже проблема возникает в самой функции set_file_content - она внутри когда принимает параметры, копирует их в локальные переменные.

Вариант 3
Следовательно нужно избежать копирования данных внутри функции set_file_content  и многие решат передавать и принимать данные по ссылке
используя токой вот вызов: set_file_content($file, \$data)
и такую реализацию функции:

sub set_file_content {
    my ($file, $data_ref) = @_;
    ....
   print FILE $$data_ref;
}


По использованию памяти - это отличный вариант, но есть другие недостатки:
1. Первый параметр передается обычным способом, второй по ссылке
2. Если код уже написан, то необходимо изменить везде код вызова функции
3. Самый существенный для меня недостаток - когда я передаю параметр по ссылке, я всегда задумываюсь, а не проделает ли функция каких либо операций с $data внутри и можно будет ли использовать переменную $data после вызова функции.

Вариант 4 (Сабж)
Так вот собственно перейдем к сабжу: прием по ссылке
Функцию set_file_content реализовываем следующим образом:

sub set_file_content {
    my ($file, $data_ref) = ($_[0],  \$_[1]);
    ....
   print FILE $$data_ref;
}

Вызов мы используем обычный - set_file_content($file, $data);

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

Почему это работает?
В перле есть понятие ссылки, а есть понятие алиаса(второго имени). Так вот, в массиве @_ содержатся алиасы на переданные параметры и модифицируя элементы @_, мы модифицируем внешние переменные.
Алиасы также используются в map, grep, foreach
Например:
foreach my $var (@array) {
    $var = 1;
}
мы присвоим  "1" каждому элементу массива @array

ЗЫ:
Когда использовать прием по ссылке, а когда передачу по ссылке? я придерживаюсь следующего правила:
some_function(\$data); - функция изменяет $data внутри
some_function($data);  - функция не изменяет $data внутри

суббота, 20 марта 2010 г.

Разрешено анонимное комментирование :)

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

четверг, 18 марта 2010 г.

Никогда не пишите так m/$var/ :)

Не многие в курсе, но такая запись m/$var/  чревата сложно находимыми багами. Сразу оговорюсь, что это актуально если $var может быть пустой.


Пример 1
my $a = "text"; 
my $b = "text2"; 
print 1 if $b =~ /text2/; 
print 2 if $a =~ //;
# В результате будет напечатано 1;



Пример 2
my $a = "text"; 
my $b = "text2"; 
print 1 if $b =~ /text3/; 
print 2 if $a =~ //;
# В результате будет напечатано 2;


Perl вместо пустого регекспа всегда вставляет предыдущий успешно совпавший регексп. Все это описано в perldoc perlre :). 

Бойтесь пустых регекспов и пишите, например так - m/(?:$var)/

вторник, 16 марта 2010 г.

Обновил Debug::LTrace до 0.02

* Обновилась документации и тесты.
* Теперь модуль без проблем инсталлируется под 5.8.x

ЗЫ: Как приятно смотреть на все эти зелененькие "PASS" в CPAN Testers Report :))

воскресенье, 14 марта 2010 г.

Не только одними feature5.10... :)



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

в 5.10.x


print qr{\Q\_\E} #выведет:(?-xism:\\_)
print "\Q\_\E"   #выведет:_


в 5.8.x

print qr{\Q\_\E} #выведет: 
(?-xism:_)


print "\Q\_\E"   #выведет: 
_


Занятно, однако ...

четверг, 11 марта 2010 г.

koorchik's Perl blog переехал с koorchik.name на koorchik.blogspot.com

Блог изначально создавался как просто страница с советами по Perl ("Perl Tips"), но в результате вырос и потребовался новый функционал, появились желающие прокомментировать посты...

Теперь koorchik's Perl blog можно найти по адресу http://koorchik.blogspot.com/ и все желающие могут оставлять комментарии. Если у кого-то возникало желание прокомментировать старый пост, то теперь можно это сделать, поскольку весь контент был полностью перенесен.

Как добавить свою аватарку на CPAN

Залил свой модуль Debug::LTrace на CPAN и смотрю у меня дефолтовая аватарка.
Начал перерывать все пункты в меню моего аккаунта на PAUSE сервере в поисках формы аплоада аватарок и все тщетно. Через 15 минут меня совсем перестал удивлять тот факт, что так мало автором на CPAN имеют свои аватарки :)).

Так вот. Решил я посмотреть в html коде откуда подгружается аватарка на CPAN и... возможно это для меня только было новоcтью :)... но это gravatar.com.

В общем решение следующее: если у меня в PAUSE юзер называется koorchik, то просто регистрирую на gravatar.com аккаунт по емейлу koorchik@cpan.org и добавляю туда свою аватарку. И все! После этого CPAN сам подхватит ее.

среда, 10 марта 2010 г.

Debug::LTrace - мой дебют на CPAN

Debug::LTrace отслеживает вызов и возврат функций. Использование Debug::LTrace не требует никаких дополнительных изменений в коде. Информации о вызовах выводится через стандартный warn.

На CPAN есть и другие модули такие, как Devel::TraceCalls и Debug::Trace, но Devel::TraceCalls неудобный в использовании(хотя очень мощный), Debug::Trace - удобный и простой, но не хватает функционала.

Debug::LTrace за основу взял удобное API Debug::Trace, но дополнительно поддерживает:

  • Лексически ограниченный трейсинг(трейсит пока существует объект-трейсер)
  • Стандартная функция caller отрабатывает нормально
  • Трейсинг целых модулей (используя '*' для обозначения всех функций в модуле)
  • Улучшенный вывод информации (дерево вложеностей)
  • Больше отладочной информации (время выполнения, контекст вызова...)
Модуль можно посмотреть http://search.cpan.org/~koorchik/Debug-LTrace-0.01/lib/Debug/LTrace.pm

Пример вывода: 
TRACE C: /-FOO::out_outer() called at example.pl line 15 package FOO
TRACE C: | /-FOO::outer(2,{'aaa' => {'yyy' => 'ARRAY(0x7fe610)','qqq' => 'www'}}) called at example.pl line 49 sub FOO::out_outer
TRACE C: | | /-FOO::inner(3) called at example.pl line 32 sub FOO::outer
TRACE C: | | | /-FOO::Dumper(3) called at example.pl line 39 sub FOO::inner
TRACE R: | | | \_FOO::Dumper(3) [VOID] in 3.5e-05 sec
TRACE C: | | | /-FOO::inner2(3) called at example.pl line 40 sub FOO::inner
TRACE R: | | | \_FOO::inner2(3) [VOID] in 1.4e-05 sec
TRACE R: | | \_FOO::inner(3) [VOID] in 0.000213 sec
TRACE C: | | /-FOO::inner2(4) called at example.pl line 33 sub FOO::outer
TRACE R: | | \_FOO::inner2(4) [VOID] in 1.5e-05 sec
TRACE R: | \_FOO::outer(2,{'aaa' => {'yyy' => 'ARRAY(0x7fe610)','qqq' => 'www'}}) [VOID] in 0.000417 sec
TRACE C: | /-FOO::inner(111) called at example.pl line 50 sub FOO::out_outer
TRACE C: | | /-FOO::Dumper(111) called at example.pl line 39 sub FOO::inner
TRACE R: | | \_FOO::Dumper(111) [VOID] in 3.5e-05 sec
TRACE C: | | /-FOO::inner2(111) called at example.pl line 40 sub FOO::inner
TRACE R: | | \_FOO::inner2(111) returned: (112) in 4.4e-05 sec
TRACE R: | \_FOO::inner(111) returned: (112) in 0.000276 sec
TRACE R: \_FOO::out_outer() [VOID] in 0.00088 sec
TRACE C: /-FOO::recurse(1) called at example.pl line 25 package FOO
TRACE C: | /-FOO::recurse(2) called at example.pl line 58 sub FOO::recurse
TRACE C: | | /-FOO::recurse(3) called at example.pl line 58 sub FOO::recurse
TRACE C: | | | /-FOO::recurse(4) called at example.pl line 58 sub FOO::recurse
TRACE C: | | | | /-FOO::recurse(5) called at example.pl line 58 sub FOO::recurse
TRACE R: | | | | \_FOO::recurse(5) returned: (6) in 4.8e-05 sec
TRACE R: | | | \_FOO::recurse(4) returned: (6) in 0.000177 sec
TRACE R: | | \_FOO::recurse(3) returned: (6) in 0.00032 sec
TRACE R: | \_FOO::recurse(2) returned: (6) in 0.00044 sec
TRACE R: \_FOO::recurse(1) returned: (6) in 0.000562 sec
TRACE C: /-BAR::Dumper([1]) called at example.pl line 70 package BAR
TRACE R: \_BAR::Dumper([1]) [VOID] in 3.8e-05 sec