среда, 22 декабря 2010 г.

Ура, товарищи. Вышел Mojolicious 1.0

Список изменений по этой ссылке

Хотя в твиттере Себастьяна видим:
"#mojolicious 1.0 is scheduled for december 26! #perl"

среда, 1 декабря 2010 г.

Чем плох eval?!

Чем плох eval?!
В perl, как всем известно, имеется 2 варианта вызова функции eval. Строковый eval для компиляции и выполнения кода на лету и блочный eval.
Нас интересует блочный eval.
Часто встречается такой вот код:


eval { 
  # some code 
  die "error"; 
  # some code  
}; 

if ($@) { 
    print "Error [$@] occured!"; 
}

Это вполне стандартный код, но есть ряд недостатков связанных и с использованием глобальной переменной $@.
Потенциальная проблема #1
Если кто-то локализировал переменную $@, то мы не сможем перехватить исключение. В этом коде мы не видим ошибки.


sub do_die { 
    die "Error"; 
} 

eval { 
    local $@;
    do_die(); 
}; 

if ($@) { 
    print "Error [$@] Occured!"; 
} 


Потенциальная проблема #2
Если в деструкторе используется eval, то скорее всего мы не перехватим исключение. Пример:

package Object;
sub new { 
    my $class = shift; 
    my $self  = {}; 
    bless $self, $class; 
}

sub DESTROY {
    eval { 
        # some clean code 
    } 
} 

package main; 
eval { 
    my $obj = Object->new(); 
    die  "Error"; 
}; 

if ($@) { 
    print "Error [$@] Occured!";
} 

  Деструктор с eval-ом может быть в ком-то модуле с CPANа и Вы просто не будете знать про это.
Потенциальная проблема #3
 $@ может содержать объект, которые в булевом контексте вернет ложь.

Решение
В связи со всем вышесказанным, рекомендую использовать  модуль Try::Tiny либо TryCatch. Они позволят Вам  избежать описанных проблем в будущем  и предоставят удобные и понятные синтаксический конструкции(try{}catch{} finally{}).  

Я использую Try::Tiny, он мне нравится своей минималистичностью и отсутствием внешних зависимостей. 

Вот пример кода с Try::Tiny. Красиво и надежно :).
# handle errors with a catch handler
try {
    die "foo";
}
catch {
    warn "caught error: $_";    # not $@
};

# just silence errors
try {
    die "foo";
};
Более доходчиво про это все написано в "perldoc Try::Tiny".

среда, 24 ноября 2010 г.

Пример приложения на Mojolicious ( не Lite )

Пример приложения на Mojolicious ( не Lite )
В интернете много всего уже сказано про Mojolicious::Lite, но сегодня про Mojolicious (не Lite). С фреймворком я только начал знакомится, поэтому смело критикуйте в комментариях. Для изучения фреймворака я решил написать прототип сервиса заметок. Сразу прошу не писать про XSRF,  я в курсе и это лишь прототип. Расписывать все я не буду, только ключевые моменты. Основную информацию Вы сможете почерпнуть непосредственно из кода.
Почему Mojolicious?
Mojolicious - очередной MVC веб-фрейворк. "Очередной велосипед, хоть и с более круглыми колесами", как кто-то он нем отозвался на одном из форумов.

четверг, 21 октября 2010 г.

Тестирование в Perl: Завершение теста при первой ошибке

Для меня стала открытием вот эта функция - Test::Most::bail_on_fail :)

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

Если вы добавите в начало теста вызов Test::Most::bail_on_fail, то при первой же неудаче тест остановится. Теперь нас интересуют лишь последние сообщения в лог файле ;)

пятница, 24 сентября 2010 г.

Современный Perl и современный Perl-программист.

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

За последние 10 лет произошло колоссальное развитие Perl . Это развитие похоже на то, как развивается Java. Сам язык программирование Java - практически не меняется, но развивается инфраструктура. Сегодня знание Java - это знание фреймворков, а не знание синтаксиса языка.

Аналогичная ситуация происходит с Perl. Я не спорю, что 5.6 и 5.12 существенно отличаются, но эти отличия скорее эволюционные, которые не меняют подхода к написанию программ. Инфраструктурное развитие языка - это и есть основная составляющая того колоссального развития Perl. Инфраструктура - это фреймворки, а фреймворки - это каркас архитектуры приложения, который задает подход. Таким образом развитие фреймворков меняет подходы к написанию приложений.

Знание CPAN - это сегодняшнее требование к профессиональному Perl-программисту. Знание CPAN - это не умения устанавливать модули, это умение применить подходящий инструмент для решения задачи.

Вот небольшой список инструментов современного Perl-программиста:

  • Moose || Mouse
  • Catalyst || Mojolicious || Dancer || Jifty || CGI::Application
  • DBIx::Class || Rose::DB::Object || ORLite
  • AnyEvent || POE || IO::Lambda
  • HTML::Template || Template::Toolkit || HTML::CTPP2
  • DateTime && Devel::NYTProf && TryCatch
Если в этом списке для Вас есть незнакомые названия, советую исправить эту проблему. После этого Вы будете вооружены и в случае чего будете знать каким инструментом можно решить проблему.

Желательно, как минимум, знать про существование таких вещей. А про возможности модулей, которые идут в поставке с Perl вы просто обязаны знать!

Вы должны знать про новые стандартные модули/прагмы (очень приятные и душевные модули ;)) :
  • Params::Check
  • Term::UI
  • Object::Accessor
  • Time::Piece
  • Time::Seconds
  • File::Fetch
  • parent
  • autodie
И не забывать про те, которые уже давно поставляются вместе с Perl: strict, warnings, lib, constant, Carp, Data::Dumper, Storable, Exporter, Encode, FindBin, Hash::Util, Scalar::Util, List::Util, File::Path,File::Copy, File::Basename, Memoize, Locale::Maketext, Sys::Hostname, Time::HiRes, CGI::Fast. Это те, которые часто приходится использовать мне (кроме Locale::Maketext, но я не мог его пропустить), а весь список "perldoc perlmodlib".
Если в это списке есть незнакомые названия, то Вы просто обязаны ввести perldoc Имя::Модуля.

PS: Хотите быть конкурентным на рынке программирования, тогда не отставайте от современного Perl ;).

четверг, 16 сентября 2010 г.

История одного коммита

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

Код изогнулся, как большой кусок оргстекла, поблескивая константами и переменными... он выдержал, он всегда выдерживал. Хотя такое случалось все чаще и чаще, но никто не обращал на это должного внимания, многим даже нравилось данное зрелище : "Так чудесно переливается в свете мониторов", - думали они. Все было точь-в-точь, как всегда и в этот раз, но что-то пошло не так... Монолитная твердая гладь заволновалась и по ней вдруг побежали волны. Казалось, еще чуть-чуть и все пойдет трещинами, расколется, разлетится на мелкие крупицы, закричат вдруг процедуры и функции и будут хвататься за острый край мироздания, моля о помощи своих друзей, напарников, но те, непонимая чего от них хотят, просто будут пялится и "укать" как слабоумные. Это был очередной Васин комит....


Через момент уже все вернулось на круги своя, только местами код, который должен быть прозрачным, как родниковая вода, помутнел, и если вглядеться, то можно было разобрать нечеткие очертания какого-то существа с большим количеством ног... или не ног. Но, как обычно, никто не обратил на это внимания ... Затем уже все привыкли и к мутностям кода. "Главное, что работает", - звучала всеми любимая фраза то здесь, то там...

среда, 8 сентября 2010 г.

GPL говорит, что я обязан открывать исходники моего сайта?

Лицензия GPL позволяет нам использовать и модифицировать код в своих целях. Мы имеем право модифицировать код и не делиться им ни с кем если мы не распространяем( не имеет значение платно или бесплатно) програмных продукт на основе этого кода.

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

Теперь относительно веб-приложений. Допустим мы создали написали фейсбук и использовали для этого модуль CGI.pm. Спрашивается должны ли мыть предоставить исходники пользователям нашим сервиса.
Вроде бы как и не должны поскольку мы не распространяем приложение, но ...

понедельник, 30 августа 2010 г.

CPANPLUS и Gentoo - Автоматически создаем ebuild-ы модулей из CPAN

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

1. Пакетный менеджер тянет по зависимостям уже установленный через cpan модуль.
2. Удаление через модулей через пакетный менеджер также не учитывает зависимости.

3. При обновлении Perl до новой версии невозможно обновить все модули через perl-cleaner.
4. ... и так далее



Решение очевидное - устанавливать все через пакетный менеджер. А если нет нужного пакета(в Gentoo - это еbuild-ы), то создавать самому.
Есть такая тулзень - "g-cpan", предназначенная для этих целей. Но у нее был ряд проблем( криво как-то работала с CPAN-репозитариями ). Возможно в новых версиях все уже исправлено.

Но сегодня мы говорим про другой инструмент, а именно про CPANPLUS. Многие уже наверное давно его оценили и пользуются командой "cpanp" вместо "cpan". 
Я же предлагаю пользоваться еще одной командой входящей в состав CPANPLUS - "cpan2dist"
Выполняем "cpan2dist --help" и смотрем для начала секцию "Examples". Как видим, мы может создавать не только ebuild-ы, но пакете в других форматах, включая deb.
Для установки Mojolicious выполняем:


cpan2dist --format=CPANPLUS::Dist::Gentoo \
--dist-opts overlay=/usr/local/portage \
--dist-opts distdir=/usr/portage/distfiles \
--dist-opts manifest=yes \
--dist-opts header="# Copyright 1999-2010 Gentoo Foundation" \
--dist-opts footer="# End" \
--ignore Data::Dumper
Mojolicious

Мы игнорируем Data::Dumper поскольку для него нет отдельного ebuild-а в системе, а он нужен по завимостям.

Пакет попадает в оверлей "/usr/local/portage". На него должна указывать переменная в файле /etc/make.conf
PORTDIR_OVERLAY="/usr/local/portage"

Теперь можем устанавливать пакет: emerge -av perl-gcpanp/Mojolicious. Возможно перед установкой придется размаскировать пакет.



Вот и все.


среда, 25 августа 2010 г.

Инкремент такой инкремент : инициализируй числовую перменную как строку и потеряй 20% скорости :)


Заголовок конечно провокационный, но бенчмарк показывает, что вариант с числовой инициализацией переменной на 20% быстрее(тестировал на 5.12).


#/usr/bin/perl -w
use v5.10;
use strict;
use warnings;

use Benchmark ':all';

cmpthese(
    -5, {
        integer_init => sub {
            my $i = 0;
            $i++ for ( 1 .. 10000 );

        },

        string_init => sub {
            my $i = '0';
            $i++ for ( 1 .. 10000 );
        },
    } );

#               Rate  string_init integer_init
#string_init  1769/s           --         -18%
#integer_init 2147/s          21%           --
    

Разгадка данной особенности довольно проста. При инициализации Perl сохраняет число, как строку. И результат инкремента сохраняет в строковом виде!!! Именно так :).

perl -MDevel::Peek -e 'my $i = "1"; print Dump $i;  $i++; print Dump $i;'
SV = PV(0x604bc8) at 0x62d318
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK)
  PV = 0x61ec50 "1"\0
  CUR = 1
  LEN = 8

SV = PV(0x604bc8) at 0x62d318
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK)
  PV = 0x61ec50 "2"\0
  CUR = 1
  LEN = 8


С декрементом такого не будет. В принципе любая числовая операция (даже числовое сравнение, например $i < 100) заставит Perl хранить число как число,  а не как строку.

for (my $i='10'; $i < 20; $i++) {} # это будет работать нормально, поскольку есть числовое сравнение.

понедельник, 23 августа 2010 г.

Улучшаем документирование кода - текстовые диаграммы.

У меня стояла задача написать документацию к программной компоненте.
И я решил поместить описание структуры классов непосредственно прямо в POD документацию.
Для этого мне потребовался инструмент, который мог бы рисовать текстовую графику.

И такой инструмент был найден - Asciio, к тому же он сам написан на Perl.
Просто инсталлируем модуль с CPAN и получаем упрощенный аналог visio для текстовых диаграмм. Запускаем /usr/bin/asciio и наслаждаемся.

Основной интерфейс

Сохраненный текстовый файл


Вот еще один примерчик


четверг, 19 августа 2010 г.

Баян, но все же - песня про Perl. Perl, in a Nutshell ! :)

вторник, 3 августа 2010 г.

Еще один способ наговнокодить.

Пишите всегда отдельно cигил и имя переменной, а лучше вообще вставляйте после сигила перевод строки. Тогда Ваш код станет значительно нечитабельней :)

Например:
my $return = "HA-HA-HA";
print $




return;
print "We are here! )";
Спешите, в Perl 6 уже так не получиться

понедельник, 14 июня 2010 г.

Фотоотчет(63 фото): YAPC-2010 День докладов

63 фотографии с Perl Mova + YAPC-2010 проходившей в Киеве.
ПЕРЕЙТИ В ФОТОАЛЬБОМ>>

ЗЫ: В picasa web-albums тоже можно оставлять комментарии ;)

суббота, 5 июня 2010 г.

Вы не любите котов? Так Вы просто не умеете их готовить.

Многие сейчас переходят с Subversion на Git. Многие считают, что merge в Subversion практически невозможно использовать... и так далее.

Эта статья для тех, кто еще пользуется Subversion, но только планирует перейти на Git.

После прочтения статьи просто взвесьте  все за и против, которые Вы получите при переходе на Git.

Я ни в коей мере не наезжаю на Git, но для меня существует ряд проблем при переходе на новую систему контроля версий:
  1. Перенести весь код с svn-репозитария в git-репозитарий с сохранением всей истории изменений.
  2. Обучить всех сотрудников работе с новой системой контроля версий.
  3. Найти адекватные инструменты для работы с Git под Windows, MacOS, Linux. Например, я использую модуль Subclipse для Eclipse и достойной замены пока не вижу. 
  4. Принять новую схему работы с системой контроля версий. На каждый баг/таск создавать ветку...
  5. Изменить скрипты деплоинга и обновления проекта.
 Все, что требуется от Вас, это оценить стоит ли оно того.

 Готовить Subversion  мы будем в два этапа ( я буду больше рассказывать "как", чем "зачем" ):
  1. Работа со своими ветками
  2. Управление релизами
Сразу замечу, что все что здесь описано, требует Subversion >= 1.6.x :
  1. Умный мердж появился только в subversion 1.5 (До этого, я соглашусь, мерджинг был неадекватный)
  2. Сокращенные пути через "^" появились в subversion 1.6
Допустим у нас есть репозитарий по адресу  http://svn.myrepo.com/
И вся основная разработка ведется в http://svn.myrepo.com/trunk. (Trunk - это ствол (в переводи с англ.))
Все дополнительные ветки будут создаваться в http://svn.myrepo.com/branches/

четверг, 3 июня 2010 г.

Удаление svn:externals для файла

Что такое svn:externals?
Это атрибут, который можно установить любому каталогу в репозитарии. Он позволяет подгружать файлы и каталоги с другого места.
(За деталями сюда - http://svnbook.red-bean.com/en/1.0/ch07s03.html)
До версии subversion-1.6 возможно было указать только ссылки на внешние каталоги. То есть внешний файл было невозможно указать.
С версии 1.6 появилась возможность указывать в svn:externals и отдельные файлы. Но существует несколько особенностей про которые нужно помнить:

  1. Невозможно указать ссылку на файл во внешнем репозитарии( а на каталог возможно ).
  2. Особенная процедура удаления ссылки на внешний файл (поскольку файл добавляется в локальную копию, как любой другой файл с репозитария).

Для удаления ссылки на внешний файл необходимо:

  1. Отредактировать либо удалить аттрибут  svn:externals и сделать коммит.
  2. Удалить папку содержащую внешний файл. Просто удаления файла - недостаточно.
  3. Обновиться для востановления удаленной папки ( "svn up" )
  4. Шаг 2 и 3 необходимо провести каждому пользователю репозитария поскольку "svn up" ничего не даст.

Вся загвоздка в пункте 4. 
Допустим у Вас был внешний файл прописанный через svn:externals, а затем Вы убрали внешнюю ссылку на файл и завели отдельную его копию. Никто и никогда про это не узнает и все будут пользоваться старым файлом пока не выполнят пункт 4.

воскресенье, 30 мая 2010 г.

Чего я не понимаю?

print 1 if "a\nb" =~ /^a\n$/m; #Почему не выводит 1?

воскресенье, 2 мая 2010 г.

Subversion: переезд на новый домен

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

Собственно сабж
У нас в компании SVN-сервер переехал на новый домен и стала задача всем разработчикам поменять имя домена для рабочей копии:
Переходим в рабочую директорию и выполняем:
svn switch --relocate old_repositary_root new_repositary_root


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

среда, 21 апреля 2010 г.

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

Вопрос:
sub a{&b; print shift}
sub b{print shift} 
a(1,2);

Ответ: 12

Пояснение:
Главная особенность состоит в том, что вызов функции &b происходит без круглых скобок и с использованием амперсанда. То есть вызов &b b(), &b() - это разные вызовы и каждый имеет свою особенность.
b() - классический вызов функции
&b() - аналогично первому варианту, но игнорируются прототипы
&b   - если функция вызывается таким образом, то в нее передается  массив @_, и я думаю, что мало кого это удивит... 
но важно то, что вызовы &b(@_) и &b - имеют отличия
в обоих случаях в функцию b придет список аргументов родительской функции, но в случае с &b(@_) это будет копия(локализированный массив), а в случае с &b, в функцию попадет оригинальный массив @_ и если сделать изменения в b, то они произойдут в @_ родительской функции.


понедельник, 19 апреля 2010 г.

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

Вопрос:
0 ? $a = 1: $b = 2;
1 ? $a = 1: $b = 2;
print "$a $b"

Ответ: 2 2 

Пояснение: 
Тут есть два важных момента:

  1. Приоритет тернарного оператора выше чем у "=" и поэтому конструкция работает так (0 ? $a = 1: $b) = 2;
  2. После вычесления тернарного оператора возвращается переменная, а не ее значение. И ее можно изменить.

Вот еще наглядный пример: 
конструкцию вида
if ( $x ) { 
    $a->{key} = 1;
}
else { 
    $b->{key} = 1; 
}
можно заменить на
($x ? $a : $b)->{key} = 1;

суббота, 17 апреля 2010 г.

Экономия памяти: YAML::Tiny

Небольшое вступление для тех, кто  еще не использует YAML (позволю себе процитировать википедию )
YAML — человекочитаемый формат сериализации данных, концептуально близкий к языкам разметки, но ориентированный на удобство ввода-вывода типичных структур данныхмногих языков программирования. 
Я использую YAML для разного рода конфигурационной информации.
Почему я использую YAML? :
  • YAML краток и понятен;
  • YAML очень выразительный и расширяемый;
  • YAML допускает простой потоковый интерфейс;
  • YAML использует структуры данных, родные для языков программирования;
  • YAML легко реализуется, возможно, слишком легко;
  • YAML использует цельную модель данных. Нет исключений — нет беспорядка.
Сабж
Идея YAML::Tiny - Читать/Писать  YAML данные с минимальными затратами + минимум внешних зависимостей(не использует ничего, кроме стандартных модулей). 
Просто заменив в коде:
use YAML 'LoadFile';
на 
use YAML::Tiny 'LoadFile'; 
я сэкономил 4МБ памяти в каждом FCGI процессе!!!

Спецификация YAML невероятно огромна.  И Pure Perl реализация - модуль  YAML поддерживает ее полностью и съедает 4МБ оперативной памяти. Yaml::Tiny мал и быстр за счет того, что он не поддерживает все возможности описанные в спецификации.  

ЗЫ: Если не хватает возможностей YAML::Tiny, тогда следует смотреть в сторону YAML::Syck


пятница, 9 апреля 2010 г.

Несостоятельность ithreads в Perl (сам не ожидал)

Предыстория
Никогда до этого не использовал треды(threads)  в Perl - как-то не доверял я им и использовал forkи... и как оказалось не зря!
И настал тот день, когда от тредов никуда не убежишь(на самом деле убежишь, но про это дальше) - нужно было включить многопоточность для модуля Fuse.pm(просто передается дополнительный параметр threaded => 1), и тогда на каждый вызов файлофай системы будет создаваться тред.

Собственно сабж:
  1. Переменные по-умолчанию не являются общими между тредами. Это означает, что каждый раз когда вы стартуете тред, все структуры данных копируются в новый тред. Все - это значит все, включая внутренности всех пакетов, глобальные переменные, лексические...
  2. Общие переменные на самом деле - не общие, а просто связанные(Tied), со всеми вытекающими последствиями - требуют больше памяти и более медленные. Подробней тут http://www.perlmonks.org/index.pl?node_id=288022
  3. Самое печальное. Треды ЛИКАЮТ!!! Каждый запущенный тред после своего завершения не возвращает часть памяти!!!
  4. Треды в Perl требуют больше памяти чем fork-и !!! Да именно так :), поскольку fork-и используют COPY-ON-WRITE при копировании процесса в памяти.
  5. Нужен Perl скомпилированный с поддержкой тредов.
Вот простой тест на лики памяти: 

Достаточно запустить и понаблюдать за потреблением памяти процессом.
use threads;
while (1) {
    threads->create(sub{})->join();
}
Решение.
Использовать fork-и :) (естественно там, где они нативно поддерживаются). По существу:
  1. Fork-и не ликают
  2. Fork-и понятней и не подвержены ошибкам связанными с семафорами и локами данных. 
  3. Сейчас в большинстве unix системах fork-и - дешевые(в Linux в том числе), поскольку используется COPY-ON-WRITE метод. То есть, при создании нового процесса содержимое памяти не копируется. Накладные расходы только на организацию нового процесса.
  4. Работают на любой версии Perl
А что если выхода нет? Как в случае c модулем Fuse.pm? Я уже думал, что придется отказаться от многопоточности, но наткнулся на модуль forks. Этот модуль являет собой прагму, которая заменяет нативную реализацию тредов на использование fork-ов. 
Представьте себе, что есть код написанный с использованием тредов, просто напишите вначале кода use forks; и код будет использовать fork-и вместо тредов ( с Fuse.pm работает ).
Вот пример( и код перестает ликать):
use forks;
use threads;

while (1) {
    threads->create(sub{})->join();
}


четверг, 1 апреля 2010 г.

Мог бы быть вопрос номер 3 на понимание Perl ...

... если бы я сам понимал что тут происходит
perl -e 'my $i=5; print(++$i + ++$i)' #  почему тут 14, а не 13 ???


perl -e 'my $i=5; print(sub{++$i}->() + sub{++$i}->())' #   тут все нормально - выдает 13



perl -e 'my $i=5; print( $i++ + $i++)' # и так нормально - выдает 11



среда, 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

воскресенье, 7 февраля 2010 г.

Таблицы псевдографикой


Задача отправлять табличную информацию на почту.  И решение в общем то очевидное - использовать псевдографику(хотя возможно кого-то устроит html), либо просто выравнивать текст при помощи printf, но эффект уже не тот :).  Полчаса моего поиска в CPAN увенчались успехом и был найдет модуль Text::SimpleTable, который был предельно простым и полностью решал поставленную задачу. 

Пример:
use Text::SimpleTable;
my $t = Text::SimpleTable->new([5, 'Foo'], [10, 'Bar']);
$t->row('foobarbaz', 'yadayadayada');
$t->hr;
$t->row('barbarbarbarbar', 'yada');
print $t->draw;


.-------+------------.
| Foo   | Bar        |
+-------+------------+
| foob- | yadayaday- |
| arbaz | ada        |
+-------+------------+
| barb- | yada       |
| arba- |            |
| rbar- |            |
| bar   |            |
'-------+------------'

четверг, 4 февраля 2010 г.

GUI в Perl: Frozen Bubble

Есть игрушка, которая мне очень нравится - FrozenBubble  и я давно в нее играюсь.  И я был приятно удивлен когда узнал, что она написана на Perl и SDL :)
Красочная игра, 100 уровней для одного игрока, есть поддержка сетевой игры, классный саундтрек.  Показательный пример создания качественной игры на Perl.


пятница, 29 января 2010 г.

Инструменты разработчика: Devel::TraceUse


Модуль позволяет посмотреть(в виде дерева) какие модули подключает ваша программа. 
Это бывает может быть полезным в разных случаях. Например,  когда  программа сразу после компиляции начинает занимать слишком много памяти...  или бывает непонятно какой из бекендов подключает некий модуль(например JSON)... или просто необходимо собрать список зависимостей  ...  В общем - вариантов может быть масса.
В любом случае попробуйте запустить программу вот так: perl -MDevel::TraceUse myprog.pl, возможно будете удивлены.

Пример вывода:
perl -MDevel::TraceUse -MFindBin -e ''
Modules used from -e:
  FindBin, line 0 (4.8e-05)
    Carp, line 95 (3.9e-05)
    Cwd, line 98 (4.1e-05)
      XSLoader, line 247 (5.3e-05)
    File::Basename, line 99 (5.6e-05)
      re, line 44 (3.8e-05)
    File::Spec, line 100 (4e-05)
      File::Spec::Unix, line 22 (3.6e-05)