среда, 27 мая 2009 г.

Сегодня без того - не знаю чего

На этот раз Perl Фея дала мне время окончательно проснуться, а не шептала сонному задание на ушко.
После предыдущей встречи, описанной в http://laziness-impatience-hubris.blogspot.com/2009/05/return_20.html, я был готов ко всему что угодно, но такого даже не мог представить. Увидев, что я открыл глаза, Фея подошла, приложила свой тоненький пальчик к кончику моего носа, загадочно подмигнула и тихо прошептала: "А сегодня без того - не знаю чего".

Я смотрел на нее как баран на новые ворота, а она села на край дивана, взяла с полки книгу о виноградарстве и стала ее неторопливо листать. Через некоторое время, видя, что я еще нахожусь в замешательстве от задания, Фея сказала: "Я подожду тут - на улице ведь ливень, а ты не отвлекайся, работай".

Прошла минута или больше, а я все еще продолжал стоять на месте как вкопанный. "Кстати, вот тут, при описании процесса формирования высокоштамбового двуплечного кордона со свисающим приростом, забыли упомянуть про резервные сучки замещения. А ты ведь недавно посадил четыре куста винограда и собираешься придавать им эту форму, так что обрати внимание на возможность быстрой замены поврежденных рукавов", - Фей взглянула в мою сторону и ее улыбка вернула мне способность мыслить. "Какая все таки разносторонне образованная Фея!", - подумал я и спросил: "Насчет задания... Я не понимаю, что ты хочешь чтобы я сделал".

"Ну как не понимаешь? Ты, наверно, еще не проснулся. В позапрошлый раз я попросила тебя сделал "без return", затем - "без return, но наоборот". Сегодня сделай что-то нечто среднее! Все очень просто", - лукаво улыбнулась она и опустила взгляд в книгу.

Легко сказать, что-то среднее... Возвращать не возвращая, вызывать не вызывая... Точно! Придумал! Надо вызывать возвращаемое, не передавая информацию как вызывать! Если бы я не был так увлечен придуманным решениям, то я бы заметил, как Фея украдкой одобрительно посмотрела на меня. Нет, феи, не способны читать мысли людей, но они способны тонко чувствовать их.

И так, через минуту код из предыдущих заданий (http://laziness-impatience-hubris.blogspot.com/2009/05/return.html, http://laziness-impatience-hubris.blogspot.com/2009/05/return_20.html) был преобразован к следующему виду:

sub mul(&\@\@) {
my ($sub, $x, $y) = @_;
my @r = map { $$x[$_] * $$y[$_] } 0 .. $#$x;
return sub { $sub->(@r) };
}

sub minus(&\@\@) {
my ($sub, $x, $y) = @_;
my @r = map { $$x[$_] - $$y[$_] } 0 .. $#$x;
return sub { $sub->(@r) };
}

sub say(&@) {
my $sub = shift;
print join(" ", @_), "\n";
return sub { $sub->() };
}

sub main() {
my @i = (1, 2, 3);
my @j = (2, 3, 4);
my @k = (3, 4, 5);

return mul { minus { say {} @_ } @_, @k } @i, @j;
}

my $sub = \&main;
$sub = $sub->() while $sub;

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

Поставив последнюю точку, хотел было позвать Фею, но она уже стояла за моей спиной. Посмотрела, одобрительно кивнула и сказала: "Ну, я полетела, тем более, что дождь закончился".

"Постой", - остановил ее я: "а каков смысл этих заданий?" "Все таки мне нельзя быть такой обворожительно красивой, ведь глядя на меня твой мысли пытаются", - игриво ответила она.

"Ну посмотри, я же вплотную подвела тебя к...", - не успела Фея закончить фразу, как я ее перебил: "... к событийно-управляемому стилю программирования БЕЗ каких либо глобальных структур данных!"
"Молодец, все верно. Стоит лишь заменить return на регистратор обработчиков, а "большую вызывалку", как ты ее смешно называешь, - на генератор или цикл ожидания событий...", - продолжила она.

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

Вот, а вы говорите, что Фей нет. Они есть, к тому же очень симпатичные и обворожительные!

среда, 20 мая 2009 г.

Сегодня без return - выворачиваем наизнанку

Похоже это уже становиться традицией - задавать задачки спозаранку.
Оказывается, существует Perl Фея, которая по утрам, когда пора вставать, будит всех Just another Perl Hacker.
О прошлом появлении Феи читайте в http://laziness-impatience-hubris.blogspot.com/2009/05/return.html

Она очень осторожна, но сегодня, устроив засаду, я ее увидел первый раз.
Фей, поняв, что я не сплю, от неожиданности смутилась, похвалила меня за решение задачи "Сегодня без return",
и тут же кокетливо спросила:
- А сможешь наоборот?!
- Это как? - удивился я.
- Ну, ведь это ты Perl Hacker, а не я - придумай, - стрельнув зелеными глазами ответила она и улетела.

Да... Озадачила...
И так, в прошлый раз мы избавились от return и это наш резерв.
Если наоборот, то возвращаем все return, и используем их чтобы, чтобы...
Неужели чтобы предотвратить вызовы подпрограммы!!!

Что-то тут не так - совсем без вызовов невозможно.
Может стоит лишь упорядочить их, централизовать в одном месте?
И будет один "большой вызов", не множество "маленьких".

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

sub mul(&\@\@) {
my ($sub, $x, $y) = @_;
my @r = map { $$x[$_] * $$y[$_] } 0 .. $#$x;
return $sub, @r;
}

sub minus(&\@\@) {
my ($sub, $x, $y) = @_;
my @r = map { $$x[$_] - $$y[$_] } 0 .. $#$x;
return $sub, @r;
}

sub say(&@) {
my $sub = shift;
print join(" ", @_), "\n";
return $sub;
}

sub main() {
my @i = (1, 2, 3);
my @j = (2, 3, 4);
my @k = (3, 4, 5);

return mul { minus { say {} @_ } @_, @k } @i, @j;
}

# А вот и "большая вызывалка", которая все это запускает.
my $sub = \&main;
my @param = ();
do {
($sub, @param) = $sub->(@param);
} while ($sub);

В вышеприведенном коде "большая вызывалка" вызывает лишь подпрограммы-клей, которые в свою очередь вызывают подпрограммы-кирпичики.
Конечно можно сделать так, чтобы она вызывала все подпрограммы:

return \&mul, sub { \&minus, sub { \&say, undef, @_ }, \@_, \@k }, \@i, \@j;

Но в этом случае, мы лишаемся магии прототипов, так что первый вариант - предпочтительней.

Чувствую, что Фея будет довольна. Интересно только, зачем она меня подталкивает в этом направлении?
Хотя, всему свое время: скоро, наверно, узнаю.

среда, 13 мая 2009 г.

Сегодня без return

Подозрения оправдались. История продолжается, после описанных в http://laziness-impatience-hubris.blogspot.com/2009/05/blog-post.html и http://laziness-impatience-hubris.blogspot.com/2009/05/2-tail-call.html
цветочков пошли ягодки.

Итак сегодня работаем без return!

В качестве примера умножим поэлементно пару векторов и отнимем третий:

(1, 2, 3) >>*<< (2, 3, 4) >>-<< (3, 4, 5)

Для этого в perl6 имеются гипероператоры - http://laziness-impatience-hubris.blogspot.com/2009/01/perl6.html.

Но сейчас разговор не о perl6, а о perl5. Вот код, который выполняет вышеупомянутые действия:

sub mul(\@\@) {
my ($x, $y) = @_;
map { $$x[$_] * $$y[$_] } 0 .. $#$x;
}

sub minus(\@\@) {
my ($x, $y) = @_;
map { $$x[$_] - $$y[$_] } 0 .. $#$x;
}

my @i = (1, 2, 3);
my @j = (2, 3, 4);
my @k = (3, 4, 5);

my @ij = mul(@i, @j);
my @r = minus(@ij, @k);
print join(" ", @r), "\n";

Хотя в этом коде return явно не прописан он присутствует.
Обе подпрограммы возвращают результаты в виде списков.

Теперь попробуем избавиться от return. Сразу говорю, что модификация передаваемого по ссылки массива, - это не наш путь.
Не для того существует perl, чтобы на нем писать с C стиле.

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

mul(@i, @j, sub { minus(@_, @k, sub { say(@_, sub {}) }) });

Или вот так, чтобы было удобней работать с параметрами подпрограмм,
ссылку на подпрограмму сделаем первой, а не последней среди передаваемых параметров:

mul(sub { minus(sub { say( sub {}, @_) }, @_, @k) }, @i, @j);

Что, при использовании магии прототипов, можно записать следующим образом:

mul { minus { say {} @_ } @_, @k } @i, @j;

Соответственно, подпрограммы в качестве первого параметра принимают ссылку на подпрограмму,
которую и вызывают, передав ей результат своей работы:

sub mul(&\@\@) {
my ($sub, $x, $y) = @_;
my @r = map { $$x[$_] * $$y[$_] } 0 .. $#$x;
$sub->(@r);
}

sub minus(&\@\@) {
my ($sub, $x, $y) = @_;
my @r = map { $$x[$_] - $$y[$_] } 0 .. $#$x;
$sub->(@r);
}

sub say(&@) {
my $sub = shift;
print join(" ", @_), "\n";
$sub->();
}


my @i = (1, 2, 3);
my @j = (2, 3, 4);
my @k = (3, 4, 5);

mul { minus { say {} @_ } @_, @k } @i, @j;

Вот и все. Поставленная задача выполнена. Теперь немного порассуждаем.

Подпрограммы в вышеприведенном коде можно разбить на два типа:
1) подпрограммы-кирпичики (mul, minus, say);
2) подпрограммы-клей (анонимные подпрограммы).

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

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

четверг, 7 мая 2009 г.

Сегодня без циклов, часть 2 - tail call оптимизация

Продолжение истории, начало - http://laziness-impatience-hubris.blogspot.com/2009/05/blog-post.html.

Сегодня я не был разбужен мыслью: "сегодня без ...".
Сегодня меня разбудило порицание: "а-я-яй, рекурсия..., а где же tail call оптимизация?!"

- Отстань, - отвечаю я: в perl нет поддержки tail call оптимизации".
- В perl5 - да, но в perl6 будет!
- Ладно, вот вариант с оптимизаций:

my @vector = 1 .. 5;

sub _sum {
my ($s, $h, @t) = @_;
@t ? _sum($s + $h, @t) : $s + $h;
}

sub sum { _sum(0, @_) }

print sum(@vector), "\n";

Краткое пояснение к коду: tail call оптимизация используется когда функция завершается вызовом другой.

Сильно подозреваю, что завтра будет продолжение! :-)

среда, 6 мая 2009 г.

Сегодня без циклов

Сегодня мое пробуждение от сладкого сна было грубо и бесцеремонно нарушено невесть откуда прилетевшей мыслью: "сегодня без циклов"!

"Без циклов, так без циклов", - в ответ подумал я.

В качестве примера рассмотрим сложение элементов массива.

my @vector = 1 .. 5;

# Вариант с циклом.
{
my $sum = 0;
$sum += $_ for @vector;
print $sum, "\n";
}

# Вариант без цикла.
{
sub sum {
my ($h, @t) = @_;
@t ? $h + sum(@t) : $h;
}

print sum(@vector), "\n";
}

Вот так просто можно обойтись без циклов, используя рекурсию.

Интересно, а вдруг и завтра я буду разбужен мыслью: "сегодня без ...", и это "без ..." будет намного сложней?
Но, как говориться, утро вечера мудренее.