shootnix's posterous http://shootnix.com Most recent posts at shootnix's posterous posterous.com Fri, 23 Mar 2012 13:05:39 -0700 Mojolicious & Mojo http://shootnix.com/mojolicious-mojo http://shootnix.com/mojolicious-mojo В рассылке моджо наблюдается забавный холивар: одни хотят Mojo
отдельно от Mojolicious, другие, в т.ч. автор, не хотят.
Ну так, забавно, как они дерутся.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Wed, 21 Mar 2012 10:20:00 -0700 Идеальное API (тащемта, например) http://shootnix.com/api-97092 http://shootnix.com/api-97092

1
$validator->check($required, $params)

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Thu, 15 Mar 2012 03:44:00 -0700 Perl, контекст и wantarray (wantlist) http://shootnix.com/perl-wantarray-wantlist http://shootnix.com/perl-wantarray-wantlist

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

1
2
3
4
sub param { return (wantarray) ? 'array' : 'scalar'; }

my $scalar = param(); # $scalar = 'scalar'
my @array = param(); # @array = ('array');

А вот интересное поведение этой же функции, для меня кажущееся крайне неожиданным. 

1
2
3
sub param { return (wantarray) ? 'array' : 'scalar'; }

my $hashref = { whois => param() }; # !! $hashref->{whois} = 'array'

 

 

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Wed, 14 Mar 2012 00:04:00 -0700 given - when & goto http://shootnix.com/given-when-goto http://shootnix.com/given-when-goto

Недавно с этим столкнулся, надо бы запомнить: goto запрещено использовать в given-when.

$ perl -E '$a = 1; given($a) { when(1) { goto ENDING } } ENDING: say "ENDING"'
panic: goto at -e line 1. 

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Wed, 25 Jan 2012 06:57:00 -0800 Скорость компиляции Perl + OOP (Несколько методов). Тупые тесты http://shootnix.com/perl-oop http://shootnix.com/perl-oop

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

Вместо этого я понаделал тупые тесты при помощи команды time perl myscript.pl. Просто для того, чтобы кое-что прояснить для себя.

1. Чистый перл, атрибуты в массиве 

Массивы в перле быстрые. Пробуем такой код:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package ArrayClass;

use v5.10;
use strict;
use warnings;

use Data::Dumper;

sub new {
    my ($class, @self) = @_;
    
    return bless \@self, $class;
}

sub name {
    my ($self, $name) = @_;
    
    if ($name) {
        $self->[0] = $name;
    }
    
    return $self->[0];
}

sub age {
    my ($self, $age) = @_;
    
    if ($age) {
        $self->[1] = $age;
    }
    
    return $self->[1];
}

1;

Для проверки код почти всегда один и тот же:

1
2
3
4
5
6
7
8
9
10
my $person = ArrayClass->new('Bill', 21);

say $person->name;
say $person->age;

$person->name('Bob');
$person->age(23);

say $person->name;
say $person->age;

Что показывает time:

real  0m0.029s
user      0m0.024s
sys       0m0.000s

2. Чистый перл, атрибуты в хеше

Хеши в перле медленные, медленнее, чем массивы, поэтому, возможно, если делать акцессоры на хеше, то будет медленнее. Пробуем:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package HashClass;

use strict;
use warnings;

use v5.10;

sub new {
    my ($class, %self) = @_;
    
    return bless \%self, $class;
}

sub name {
    my ($self, $name) = @_;
    
    if ($name) {
        $self->{name} = $name;
    }
    
    return $self->{name};
}

sub age {
    my ($self, $age) = @_;
    
    if ($age) {
        $self->{age} = $age;
    }
    
    return $self->{age};
}

1;

Время выполнения:

real       0m0.033s
user      0m0.020s
sys       0m0.008s

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

3. Используем Mojo::Base

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

1
2
3
4
5
6
7
8
9
package MojoClass;
    
use Mojo::Base -base;
use Data::Dumper;

has 'name';
has 'age';

1;

Код сократился, а время выполнения? Смотрим:

real       0m0.032s
user      0m0.024s
sys       0m0.000s

Вообще, в среднем, такой код получается сравним по скорости с предыдущим, или, если все же придираться, то чууууть-чуть медленнее. Но есть Mojo::Base::XS, который, возможно, решит такую проблему. Неожиданно приятно.

4. Используем Class::Struct

Class::Struct очень старый, но, тем не менее, очень известный модуль, к которому приложились довольно известные, именитые перл-программисты. Поэтому его было тоже интересно.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package StructClass;

use v5.10;
use strict;
use warnings;

use Data::Dumper;
use Class::Struct;

struct(
    name => '$',
    age => '$',
);

1;

В итоге:

real       0m0.050s
user      0m0.012s
sys       0m0.016s

В целом медленнее, причем заметно. 

5. Используем Moose

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

1
2
3
4
5
6
7
8
9
10
11
package MooseClass;

use Data::Dumper;
use Moose;

has 'name' => ( is => 'rw', isa => 'Str' );
has 'age' => ( is => 'rw', isa => 'Int' );

__PACKAGE__->meta->make_immutable;

1;

Шок:

real       0m0.419s
user      0m0.320s
sys       0m0.032s

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

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Wed, 18 Jan 2012 06:52:00 -0800 Mojolicious::Plugin::AnyData-1.20 http://shootnix.com/mojoliciouspluginanydata-120 http://shootnix.com/mojoliciouspluginanydata-120

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

Загрузка таблиц теперь происходит в любом месте программы, благодаря новому хелперу any_data, который доступен теперь для вызова методов load_data и func в любом месте кода, как экшенов, так и темплейтов (хе-хе), а не только во время запуска приложения, как раньше.

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

На гитхабе: https://github.com/shootnix/Mojolicious-Plugin-AnyData

На CPAN'e: http://search.cpan.org/~shootnix/Mojolicious-Plugin-AnyData-1.20/lib/Mojolicious/Plugin/AnyData.pm

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

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Fri, 06 Jan 2012 00:06:00 -0800 Параметры функции в Perl, небольшая зарисовка http://shootnix.com/perl-94330 http://shootnix.com/perl-94330

Если внутри функции использовать массив @_, то это повлияет на целостность данных вне функции:

1
2
3
4
5
6
7
my $a = 1;
sub change_a {
    $_[0]++;
}
change_a($a);

say $a; # 2

Если из массива вынимать данные и использовать их, то с внешними переменными все будет в порядке:

1
2
3
4
5
6
7
my $a = 1;
sub change_a {
    (my $var = shift)++;
}
change_a($a);

say $a; # 1

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

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Sat, 17 Dec 2011 13:06:00 -0800 Mojolicious::Plugin::AnyData http://shootnix.com/mojoliciouspluginanydata http://shootnix.com/mojoliciouspluginanydata

Какое-то время назад я, просматривая на досуге один фреймворк на джаве, был обрадован интересной фиче — возможности прямо из yaml'а грузить в память некоторые структурированные данные и работать с ними с помощью ORM (Hybernate, кажется) так, как будто это данные, взятые из базы данных.

Тогда я подумал, нет ли способа организовать что-то похожее для Mojolicious. Был найден годный модуль DBD::AnyData, осталось только сделать плагин. Помимо всего прочего, добавлено несколько методов (пока только два) для удобства пользования плагином. 

Ссылка: https://github.com/shootnix/Mojolicious-Plugin-AnyData. Модуль пока не публикую на спане, желая для начала собрать некоторое кол-во багрепортов и предложений по улучшению. Отсюда просьба: критикуйте, замечайте, высказывайтесь, буду очень рад любому фидбэку. Ну и от посильной помощи не откажусь ;-)

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Thu, 01 Dec 2011 09:28:00 -0800 Динамические грабли Perl http://shootnix.com/perl-73335 http://shootnix.com/perl-73335

Известно, что Perl — динамический язык. Кроме всего прочего это означает создание переменных «на лету». Бороться с внезапно созданными переменными из-за простой опечатки мы знаем как — use strict. 

Но динамически создаются, как мы знаем, не только переменные, но и ключи хэша или ссылки на хэш, в этом случае use strict не помогает.

Допустим такую задачу: у вас есть массив, из которого нужно взять пять элементов. В качестве элементов у нас — хэш. Задача решается просто:

1
2
3
4
my @buf;
for my $i (1..5) {
    push @buf, $arrayref->[$i]->{somekey};
}

Думаю, объяснять этот пример не нужно, однако, есть условие, которое довольно просто не заметить: что, если $arrayref был изначально пуст? Что станет с ним после этих манипуляций? Правильно, он будет заполнен! 

В данном конкретном случае Perl заполнит $arrayref пустыми хэшами, которые создались динамически при помощи вызова $arrayref->[$i]->{somekey}.

1
2
3
4
5
6
7
8
$VAR1 = [
    undef,
    {},
    {},
    {},
    {},
    {}
];

Что делать, если это не входит в наши планы? Самый простой и надежный способ, конечно, следить внимательно за всем самому, не допуская подобного со стороны Perl'a. Но, немного порывшись на спане, было найдено решение для тех, кто ценит заботу о своем коде со стороны автоматических программ и решений:

1
no autovivification;

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

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Wed, 30 Nov 2011 10:19:00 -0800 Игра с условиями в Perl http://shootnix.com/perl-71020 http://shootnix.com/perl-71020

Например, такая задача. У нас есть переменная, в которую нужно поместить или значение по-умолчанию или значение из функции. Функция возвращает или значение, или undef. В дополнение ко всему, есть два замечания:

1. Функция или вызывается или нет, т.е. это дополнительное условие.

2. Переменная должна _всегда_ иметь положительное значение, т.е., если функция возвратит undef, нужно поместить в нее значение по-умолчанию.

Вариантов решения много. Самый простой вариант напрашивается еще до того, как прочитаешь задачу до конца.

1
2
3
4
5
6
7
my $value;
if ($use_function) {
    $value = function();
}
else {
    $value = 'default value';
}

Недостаток только один: нужно еще проверить, что содержит $value на выходе и присвоить значение по-умолчанию, если там undef.

Конечно, этот вариант быстро правится на такой:

1
2
3
4
5
6
7
my $value;
if ($use_function) {
    $value = function() || 'default value';
}
else {
    $value = 'default value';
}

Тут в случае, если function() вернет undef, мы имеем возможность подкорректировать значение переменной «на лету». Немного смущает такое количество строк в решении, в сущности, тривиальной задачи, поэтому перепишем с использованием тернарного оператора:

1
my $value = ($use_function) ? function() || 'default value' : 'default value';

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

1
my $value = ( ($use_function) ? function() : undef ) || 'default value';

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Thu, 17 Nov 2011 09:47:00 -0800 Как получить массив определенной длины из элементов другого, рандомно? http://shootnix.com/80541017 http://shootnix.com/80541017

Задача: есть массив, например, ['a'..'z']. Нужно софрмировать из элементов этого массива другой массив, заданной длины. 

Одно важное замечание: элементы в новом массиве не должны повторяться. И еще: получить элементы нужно рандомно.

1
2
3
4
5
6
7
8
9
10
my $ary = ['a'..'z'];

my $limit = 5;
my $count = scalar @$ary;
my $new_ary = [];
for (1..$limit) {
    my $rand_index = int rand $count - 1;
    push @$new_ary, splice @$ary, $rand_index, 1;
    $count--;
}

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Mon, 07 Nov 2011 00:05:00 -0800 Как смержить/добавить новые элементы в ссылку на хэш? http://shootnix.com/78974577 http://shootnix.com/78974577

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

1
2
3
4
5
6
7
8
9
my $hash = { a => 1, b => 2 };
say Dumper { c => 3, %$hash };

# результат:
$VAR1 = {
          'c' => 3,
          'a' => 1,
          'b' => 2
        };

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Tue, 25 Oct 2011 00:52:00 -0700 Подсветка синтаксиса в posterous http://shootnix.com/posterous http://shootnix.com/posterous

На самом деле, подсветка синтаксиса не нужна, постерус поддерживает gist, нужно просто поставить линк в нужное место и всего делов.

1. Создать гист на gist.github.com

2. Вставить прямую ссылку на gist внутрь поста. 

3. Профит!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev
Mon, 24 Oct 2011 23:49:00 -0700 Perl: хеши, массивы http://shootnix.com/perl http://shootnix.com/perl

Как выудить из массива хэшей по одному элементу хэша? Вот так:

1
my @array = map { $_->{what_we_need} } @array_of_hashes;

Или так:

1
2
my @array;
push @array, $_->{what_we_need} for @array_of_hashes;

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1576959/Rolling_Stones_05.jpg http://posterous.com/users/hdp0kw9RWgTii Alexander Ponomarev shootnix Alexander Ponomarev