perl передача параметров в функцию
Иллюстрированный самоучитель по Perl
Передача параметров
Массив @_ является локальным для данной подпрограммы, но его элементы – это псевдонимы действительных скалярных параметров. Изменение элемента массива @_ вызывает изменение соответствующего действительного параметра.
В языках программирования различают передачу параметров по ссылке и по значению. При передаче параметров по значению подпрограмма получает копию переменной. Изменение копии внутри подпрограммы не влияет на ее оригинал. При передаче параметров по ссылке подпрограмма получает доступ к самой переменной и может ее изменять.
Передача параметров через специальный массив @_ фактически является передачей параметров по ссылке. В языке Perl можно реализовать передачу параметров по значению, если внутри подпрограммы при помощи функции ту о объявить локальные переменные и присвоить им значения фактических параметров из массива @_, как это сделано в следующем примере.
Передача по ссылке параметров-массивов
Итак, подпрограмма получает и возвращает параметры через специальный массив @_. Если параметр является массивом или хеш-массивом, его элементы также сохраняются в массиве параметров @_. При передаче в подпрограмму нескольких параметров-массивов или хеш-массивов они утрачивают свою целостность. Иными словами, после записи параметров-массивов (хеш-массивов) в массив @_ из него невозможно выделить отдельный параметр-массив (хеш-массив): все параметры в массиве @_ хранятся единой «кучей». Для сохранения при передаче в подпрограмму целостности массива или хеш-массива существуют два основных подхода.
Использование типа typeglob
Первый подход, более старый, заключается в использовании внутреннего типа данных, называемого typeglob. Принадлежность к типу typeglob обозначается префиксом «*». Префикс «*» можно рассматривать как метасимвол, вместо которого может стоять любой из префиксов «$», «@», «%», «&», обозначающих тип данных «скаляр», «массив», «хеш-массив», «функция» соответственно. Интерпретатор преобразует переменную типа typeglob, например, *abc, в скалярную величину. Эта величина является ссылкой на гнездо в таблице символов, содержащее элементы, разных типов с одинаковым именем abc, и представляет любой из этих элементов.
(Таблицы символов обсуждаются в части 12)
Передача в подпрограмму вместо параметра-массива или хеш-массива соответствующей переменной типа typeglob является имитацией передачи параметра-массива (хеш-массива) по ссылке с сохранением его целостности. Рассмотрим следующий пример.
Perl передача параметров в функцию
И снова говорю вам ЗДРАВСТВУЙТЕ! Если вы это читаете, значит продержались, к моей большой радости, до 9-го шага. Сегодня мы будем рассматривать достаточно трудные моменты языка. Если кому-то что-то будет непонятно (или было непонятно в предыдущих шагах), то пишите мне и я постраюсь объяснить. Если писем с «непонятками» на какой-то шаг наберётся определённое кол-во, то я буду переписывать этот шаг в более понятной форме. Поехали (запил кофеём)!
Для применения подпрограммы ее необходимо определить либо в текущем модуле (файле), либо во внешнем модуле (файле). Объявляются подпрограммы с помощью ключевого слова sub. Чтобы использовать подпрограмму объявленную во внешнем модуле, нужно перед её использованием указать интерпретатору где её искать следющим образом use имя_модуля qw(подрограмма) или require имя_файла. Как происходит вызов подпрограмм посмотрим на примере
Cимвол & означает, что после него идёт вызов подпрограммы. Т.е. в данном случаем вызов подпрограммы test(). Но можно даже обойтись вообще без него. Теперь ещё одна тонкость. Обойтись можно и без скобок. Выглядеть это будет так:
Ощутили? Поехали дальше 🙂 Подпрограммы могут быть объявленны как перед их вызовом, так и после. Подпрограммы могут возвращать какое-то значение. Для этого служит оператор return, он возвращает идущее за ним значение.
Чаще всего указатели служат для хранения хэндлера. Что такое хэндлер мы скоро рассмотрим. Операция взятия адреса в Perl обозначается сиволом ‘\‘. Небольшой пример:
А сейчас рассмотрим как вынести свои подпрограммы в отдельный файл. Для этого достаточно сохранить свою подпрограмму в файл (предположим «test.pl»), а в том файле, в котором стоит вызов подпрограммы написать require «test.pl». Как делать модули мы узнаем позже.
Процедуры и функции
Процедуры и функции
При создании программы разработчику, как правило, приходится писать в разных ее местах практически идентичные последовательности операторов, выполняющие одни и те же функции. В подобной ситуации очень могут помочь процедуры и функции, которые представляют собой самостоятельные фрагменты программы, оформленные особым образом и снабженные уникальным именем. Упоминание этого имени в тексте программы называется вызовом процедуры (функции). В Perl, как и в «классических» языках программирования, отличие функции от процедуры заключается в том, что в результате ее исполнения всегда получается единственное значение. Поэтому обращение к функции можно использовать в выражениях наряду с переменными. Далее будем называть функцию или процедуру общим именем «подпрограмма».
Подпрограммы позволяют разбить программу на несколько независимых друг от друга частей. Это экономит память, поскольку каждая подпрограмма существует в единственном экземпляре, а обращаться к ней можно сколь угодно часто из разных точек программы. При вызове подпрограммы активизируется последовательность образующих ее операторов.
Ранее уже были описаны встроенные функции (подпрограммы), например chdir, print, mkdir, chomp, rmdir и другие. Теперь же рассмотрим функции, которые пользователь может определить самостоятельно. В Perl подпрограмма представляется конструкцией типа
Одна подпрограмма может вызывать другую, та, в свою очередь, третью и т. д. Результат, полученный после вызова подпрограммы, называется возвращаемым значением. Он выходит вследствие выполнения оператора return или последнего вычисленного выражения. Вызов подпрограммы почти всегда является частью некоторого выражения, это хорошо видно на приведенных ниже примерах.
Подпрограмма также может передавать параметры в другую подпрограмму, не «теряя» значения собственной переменной @_. Вложенный вызов подпрограммы точно так же пользуется уже своей переменной с именем @_.
Подсчитаем факториал числа 5 (листинг 2).
Для сложения чисел от 1 до 5 вместо
Следует также иметь в виду, что все операции my должны быть размещены в начале подпрограммы.
В некоторых программах на Perl можно встретить функцию local. С ее помощью создаются так называемые «полулокальные» переменные. Приведем пример использования local в программе (листинг 4).
в начало любой подпрограммы, где есть такое имя. Это сохраняет предыдущее значение и автоматически восстанавливает его при выходе из подпрограммы. В большинстве же остальных случаев опытные программисты на Perl рекомендуют применять my, а не local, так как эта операция работает быстрее и надежнее.
Функции в Perl
В Perl заложено огромное количество возможностей, которые на первый взгляд выглядят лишними, а в неопытных руках могут вообще приводить к выдаче багов. Доходит до того, что многие программисты, регулярно пишущие на Perl, даже не подозревают о полном функционале данного языка! Причина этого, как нам кажется, заключается в низком качестве и сомнительном содержании литературы для быстрого старта в области программирования на Perl. Это не касается только книг с Ламой, Альпакой и Верблюдом (“Learning Perl”, “Intermediate Perl” и “Programming Perl”) — мы настоятельно рекомендуем их прочитать.
В этой статье мы хотим подробно рассказать о маленьких хитростях работы с Perl, касающихся необычного использования функций, которые могут пригодится всем, кто интересуется этим языком.
Как работают функции Perl?
В большинстве языков программирования описание функций выглядит так:
На первый взгляд всё просто и понятно. Однако вызов данной функции в таком виде:
… приведёт к различным ошибкам, суть которых будет сведена к тому, что в функцию передано неверное количество аргументов.
Функция в Perl может быть записана так:
Суть этой записи сводится к тому, что мы написали функцию, которая возвращает 1.
Но в этой записи не указано, сколько аргументов принимает данная функция. Именно поэтому ничего не мешает вызвать её вот так:
И всё прекрасно выполняется! Это происходит потому, что в Perl передача параметров в функцию
сделана хитро. Perl славится тем, что у него много «непонятных» специальных переменных.
К тому же, в Perl параметры передаются по ссылке. В каждой функции доступна специальная переменная @_, которая является массивом входящих параметров. Очень часто в функциях пишут следующее:
Дело в том, что в Perl многие функции при вызове без аргументов используют переменные по умолчанию. Shift же по умолчанию достаёт данные из массива @_. Поэтому записи:
… совершенно эквивалентны, но первая запись короче и очевидна для Perl-программистов, поэтому используется именно она.
Второй способ получения данных — присваивание списком. В Perl мы можем сделать так:
Что есть обычное списочное присваивание.
… работает точно так же. А теперь внимание! Грабли, на которые рано или поздно наступает каждый Perl-программист:
Как мы уже говорили, в Perl параметры передаются по ссылке. Это значит, что мы можем из функции модифицировать параметры, которые в неё переданы.
Результат будет 6. Однако в Perl можно сделать в каком-то роде «передачу по значению» вот так:
А вот теперь результат будет 5.
И последние два нюанса, которые очень важны. Во-первых, Perl возвращает из функции результат последнего выражения.
Возьмём код из предыдущего примера и немного его модифицируем:
И второй важный момент, хотя, скорее, предостережение. Потенциальная проблема звучит так: «если в теле функции вызывается другая функция с амперсандом и без скобок, то эта другая функция получает на вход параметры той функции(@_), в теле которой она вызывается».
Однако, если ЯВНО указать, что функция вызывается без параметров, то всё в порядке.
И вывод будет выглядеть вот так:
$VAR1 = [];
Анонимные функции
Анонимные функции объявляются в месте использования и не получают уникального идентификатора для доступа к ним. При создании они либо вызываются напрямую, либо ссылка на функцию присваивается переменной, с помощью которой затем можно косвенно вызывать данную функцию.
Элементарное объявление анонимной функции в Perl:
Анонимные функции можно и нужно использовать как для создания блоков кода, так и для замыканий, о которых речь дальше.
Замыкания
Замыкание — это особый вид функции, в теле которой используются переменные, объявленные вне тела этой функции (не в качестве её параметров, а в окружающем коде) в лексической области видимости.
В записи это выглядит как, например, функция, находящаяся целиком в теле другой функции.
Замыкания использовать полезно, например, в той ситуации, когда необходимо получить функцию с уже готовыми параметрами, которые будут в ней сохранены. Или же для генерации функции-парсера. Колбеков.
Неизменяемый объект
Нет ничего проще, чем реализовать неизменяемый объект на Perl. Немногие современные языки могут похвастаться настолько простым и изящным решением. В Perl для этого используется state.
Фактически state работает как my — у них одинаковая область видимости. Но при этом переменная state никогда не будет переинициализирована в рамках программы. Это иллюстрируют два примера.
Эта программа напечатает 1, затем 2.
Бесскобочные функции
На наш взгляд, это самый подходящий перевод термина parenthesis-less.
Например, print часто пишется и вызывается без скобок. Возникает вопрос, а можем ли мы тоже создавать такие функции?
Безусловно. Для этого у Perl есть даже специальная прагма — subs. Предположим, что мы хотим напечатать OK, если определенная переменная true. В Perl (как и в C) нет такого типа данных, как Boolean, вместо него выступает неложное значение, например, 1.
Данная программа напечатает OK. Но это не единственный способ. Perl хорошо продуман, поэтому, если мы реструктуризируем нашу программу и приведём её к такому виду:
… то результат будет тот же. Закономерность здесь следующая: мы можем вызывать функцию без скобок в нескольких случаях:
— используя прагму subs;
— написав функцию ПЕРЕД её вызовом;
— использовать прототипы функций.
Обратимся к последнему варианту.
Прототипы функций
Стоит пояснить, как функции работают в Perl.
Зачастую разное понимание цели этого механизма приводит к холиварам с адептами других языков, утверждающих, что «у перла плохие прототипы». Так вот, прототипы в Perl не для жёсткого ограничения типов параметров, передаваемых функциям. Это подсказка для языка: как разбирать то, что передаётся для функции.
Авторы из PerlMonks объясняли это как “parameter context templates” — шаблоны контекста параметров. Детали на примерах ниже.
Есть, к примеру, абстрактная функция, которая называется my_sub:
Мы её вызываем следующим образом:
Функция напечатает следующее:
1, 2, 3, 4, 5,
Получается, что в любую функцию Perl можно передать любое количество аргументов. И пусть сама функция разбирается, что мы от неё хотели.
В функцию передается «текущий массив», контекстная переменная. Поэтому запись вида:
… означает то же самое, что и:
Предполагается, что должен быть механизм контроля переданных в функцию аргументов. Эту роль и выполняют прототипы.
Функция Perl с прототипами будет выглядеть так:
Если же мы попробуем вызвать её вот так:
… то получим ошибку вида:
Not enough arguments for main::my_sub at pragmaticperl.pl line 7, near «()»
Execution of pragmaticperl.pl aborted due to compilation errors.
… то проверка прототипов не будет происходить.
Резюмируем. Прототипы будут работать в следующих случаях:
— Если функция вызывается без знака амперсанда (&). Perlcritic (средство статического анализа Perl кода), кстати говоря, ругается на запись вызова функции через амперсанд, то есть такой вариант вызова не рекомендуется.
— Если функция написана перед вызовом. Если мы сначала вызовем функцию, а потом её напишем, при включённых warnings получим следующее предупреждение:
main::my_sub() called too early to check prototype at pragmaticperl.pl line 4
Ниже пример правильной программы с прототипами Perl:
В Perl существует возможность узнать, какой у функции прототип. Например:
Локальные переменные или динамическая область видимости
Допустим, у нас есть скрипт, который что-то считает. Вдруг нам в функции, например, понадобилась локальная переменная, которая должна быть копией глобальной.
Мы можем это сделать следующим образом:
Вывод ожидаемый:
x: 1
y: 2
x: 1
Однако в данном случае мы можем обойтись без y. Решение выглядит так:
Эта штука и называется динамической областью видимости. Очень хорошо этот пример помогает понять представление переменной в виде карточек: local — это когда мы закрываем то, что написано на карточке, другой карточкой, а как только выходим из блока, всё возвращается на круги своя. Другая аналогия от perlmonks: my — in space, local — in time. Также очень часто эта конструкция используется внутри блоков кода, в которых необходим автосброс буфера. Тогда можно сделать:
Или, если лень писать в конце каждого print n:
Оверрайд методов
Оверрайд — часто довольно полезная штука. Например, у нас есть модуль, который писал некий N. И всё в нём хорошо, а вот один метод, допустим, call_me, должен всегда возвращать 1, иначе беда, а метод из базовой поставки модуля возвращает всегда 0. Код модуля трогать нельзя.
Пусть программа выглядит следующим образом:
Которая выведет:
call_me from TOP called!
OKAY 🙁
И снова у нас есть решение:
Однако для временного оверрайда мы можем воспользоваться ключевым словом local. И тогда оверрайд будет выглядеть так:
Это заменит функцию пакета Top — call_me в лексической области видимости (в текущем блоке).
И теперь наш вывод будет выглядеть примерно следующим образом:
Overrided subroutine called!
Purrrrfect
Код модуля не меняли, функция теперь делает то, что нам надо.
На заметку: если приходится часто использовать данный приём в работе — налицо архитектурный косяк и вообще эта ситуация уже описывалась картинкой. Хороший пример использования — добавление дебаг-информации в функции.
Wantarray
В Perl есть такая полезная штука, которая позволяет определить, в каком контексте
вызывается функция. Например, мы хотим, чтобы функция вела себя следующим образом:
когда надо возвращала массив, а иначе — ссылку на массив. Это можно реализовать, и
к тому же очень просто, с помощью wantarray. Напишем простую программу для демонстрации:
Что выведет:
ARRAY!
$VAR1 = 1;
$VAR2 = 2;
$VAR3 = 3;
REFERENCE!
$VAR1 = [
1,
2,
3
];
Посему, если применять my %hash = my_cool_sub(), будет использована ветка логики wantarray. И именно по этой причине wanthash нет.
Autoload
В Perl одна из лучших систем управления модулями. Мало того что программист может контролировать ВСЕ стадии исполнения модуля, так ещё существуют интересные особенности, которые делают жизнь проще. Например, Autoload.
Суть Autoload в том, что когда функции в модуле не существует, Perl ищет функцию Autoload в этом модуле, и только затем, когда не находит, активируется исключение. Это значит, что мы можем описать обработчик ситуаций, когда вызывается несуществующая функция.
Вывод этой программы:
Hello!
Autoload::Demo::asdfgh called! with params: 1, 2, 3
Autoload::Demo::qwerty called! with params:
Генерация функций на лету
Допустим, мы хотим сделать функцию, которая должна что-то возвращать. Затем функцию, которая должна возвращать что-то другое. У объекта. Getter, так сказать. Это Perl. «Лень, нетерпение, надменность» (Л. Уолл). Я думаю, что написание кода следующего вида никому не доставляет удовольствия.
Функции можно генерировать. В Perl есть такая штука как тип данных typeglob. Наиболее точный перевод названия — таблица имён. Typeglob имеет свой сигил(*).
Для начала посмотрим код:
getOther => *MyCoolPackage::getOther
getName => *MyCoolPackage::getName
getAge => *MyCoolPackage::getAge
В принципе, глоб — это хэш с именем пакета, в котором он определен. Он содержит в качестве ключей элементы модуля + глобальные переменные (our). Логично предположить, что если мы добавим в хэш свой ключ, то этот ключ станет доступен как обычная сущность. Воспользуемся генерацией функций для генерации данных геттеров.
И вот что у нас получилось:
Эта программа напечатает:
Name: justnoxx
Age: 25
Other: perl programmer
Атрибуты функций
В Python есть такое понятие как декоратор. Это такая штуковина, которая позволяет «добавить объекту дополнительное поведение».
Да, в Perl декораторов нет, зато есть атрибуты функций. Если мы откроем perldoc perlsub и посмотрим на описание функции, то увидим любопытную запись:
Таким образом, функция с атрибутами может выглядеть так:
Работа с атрибутами в Perl — дело нетривиальное, потому уже довольно давно в стандартную поставку Perl входит модуль Attribute::Handlers.
Дело в том, что атрибуты из коробки имеют довольно много ограничений и нюансов работы, так что, если кому-то интересно, можно обсудить в комментариях.
И это приемлемое решение.
Но может возникнуть такая ситуация, что функций будет становиться больше и больше. А в каждой делать проверку будет всё накладнее. Проблему можно решить на атрибутах.
В данном примере вызов программы будет выглядеть так:
YOU SHALL NOT PASS at myattr.pl line 18. main::__ANON__() called at myattr.pl line 6
А если мы заменим return 0 на return 1 в is_auth, то:
I am called only for auth users!
Не зря атрибуты представлены в конце статьи. Для того чтобы написать этот пример, мы воспользовались:
— анонимными функциями;
— оверрайдом функций;
— специальной формой оператора goto.
Несмотря на довольно громоздкий синтаксис, атрибуты успешно применяются в Catalyst. К тому же не стоит забывать, что они, всё-таки, являются экспериментальной фичей Perl, а потому их синтаксис может меняться.
Функциональное программирование на Perl в примерах
В данной статье будет рассмотрено функциональное программирование на примере скрипта поиска битых ссылок с использованием AnyEvent::HTTP. Будут рассмотрены следующие темы:
Анонимные подпрограммы
Анонимная подпрограмма объявляется также, как и обычная, но между ключевым словом sub и открывающей скобкой блока программного кода нет имени. Кроме того, такая форма записи расценивается как часть выражения, поэтому объявление анонимной подпрограммы должно завершаться точкой с запятой или иным разделителем выражения, как и в большинстве случаев:
Например, реализуем подпрограмму, утраивающую переданное ей значение:
Основное преимущество анонимных подпрограмм — использование «кода как данных». Другими словами, мы сохраняем код в переменную (например, передаем в функцию в случае колбеков) для дальнейшего исполнения.
Пример использования рекурсии в сочетании с колбеками будет показан ниже при рассмотрении задачи поиска битых ссылок.
Замыкания (Closures)
Как сказано в википедии
Замыкание — это функция первого класса, в теле которой присутствуют ссылки на переменные, объявленные вне тела этой функции в окружающем коде и не являющиеся ее параметрами.
По сути, замыкание — это аналог класса в ООП: предоставляет функциональность и данные связанные и упакованные вместе. Рассмотрим пример замыкания в Perl и класса в C++:
Проведем анализ приведенного кода:
объявление приватной переменной:
объявляем переменную multiplier типа int после маркера доступа private ;
инициализирование приватной переменной:
при создании переменной инициализируем переданным значением;
перегружаем конструктор, чтобы он принимал число и в списке инициализации инициализируем переменную multiplier ;
создание подпрограммы, перемножающей переданное ей значение с ранее инициализированной переменной:
Для использования замыкания в Perl и класса в C++, их нужно определить, т.е. создать объект:
Аналогом функции-замыкания из примера на Perl, в C++ является использование лямбд, как показано в коде. В примере на Perl функция-замыкание, передаваемая в функцию, также называется колбеком, или функцией обратного вызова.
Функции обратного вызова (Callback)
По сути, функция обратного вызова является аналогом полиморфизма функций, а именно, позволяет создавать функции более общего назначения вместо того, чтобы создавать серию функций, одинаковых по структуре, но отличающихся лишь в отдельных местах исполняемыми подзадачами. Рассмотрим пример задачи чтения из файла и записи в файл. Для этого с помощью Perl создадим две функции reader и writer (за основу был взят пример с презентации Михаила Озерова Ленивые итераторы для разбора разнородных данных), а с помощью C++ мы создадим классы Reader_base, Writer_base, ReaderWriter.
Компилировать следующим образом:
Проведем анализ кода:
работа с созданными функциями в Perl и классами в C++:
Далее рассмотрим комплексную практическую задачу поиска битых ссылок с помощью AnyEvent::HTTP, в котором будут использоваться вышеописанные темы — анонимные подпрограммы, замыкания и функции обратного вызова.
Задача поиска битых ссылок
Для того, чтобы решить задачу поиска битых ссылок (ссылок с кодами ответа 4xx и 5xx), необходимо понять, как реализовать обход сайта. По сути, сайт представляет из себя граф ссылок, т.е. урлы могут ссылаться как на внешние страницы, так и на внутренние. Для обхода сайта будем использовать следующий алгоритм:
Затем создаем функцию scan_website, в которую передаем ограничение на максимальное количество урлов на скачивание и колбек.
Далее создаем анонимную функцию и выполняем её. Запись вида
или удаления циклической ссылки:
Структура хэша %urls следующая:
В функции process_page мы сохраняем полученный хэш и колбек.
После чего мы в цикле проходимся по хэшу урлов, получая пару (parent_url => current_urls) и далее проходимся по списку текущих урлов (current_urls)
Прежде, чем приступить к рассмотрению получения данных со страниц сделаем небольшое отступление. Базовый алгоритм парсинга страницы и получения с нее урлов предпоолагает один HTTP-метод GET, вне зависимости, внутренний этот урл или внешний. В данной реализации было использовано два вызова HEAD и GET для уменьшения нагрузки на сервера следующим образом:
Итак, сначала мы выполняем функцию http_head модуля AnyEvent::HTTP, передавая ему текущий урл, параметры запроса и колбек.
В колбеке мы получаем заголовки (HTTP headers)
из которых получаем реальный урл (урл после редиректов)
Далее, если произошла ошибка (коды статуса 4xx и 5xx), то выводим ошибку в лог и сохраняем заголовок в хэш для дальнейшего использования
Иначе, если сайт внутренний и это веб-страница,
В колбеке функции http_get получаем заголовки и тело страницы, декодируем страницу.
С помощью модуля Web::Query выполняем парсинг страницы и получение урлов.
На каждой итерации метода each мы получаем в колбеке ссылку
и преобразовываем ее
Далее мы проверяем — если в графе нет такой ссылки
то получаем корневой домен ссылки (либо ставим его в ‘fails’)
т.е. в данном случае, и родительской, и текущей страницей была стартовая страница (в остальных случаях данные страницы различаются).
Конструирование данной структуры происходит следующим образом — если сайт внутренний, то мы создаем структуру
иначе, если сайт внутренний, то структуру
Если мы не попали ни в один из вариантов (ошибка или парсинг внутренней страницы), т.е. сайт внешний и без ошибок, то выполняем колбек
Иначе, если есть урлы для проверки,
то рекурсивно вызываем анонимную подпрограмму
В качестве бонуса, в скрипте реализован вывод графа с помощью GraphViz в разные форматы — svg, png и т.д. Примеры запуска скрипта:
Также имется возможность управлять выводом логов с помощью переменной окружения PERL_ANYEVENT_VERBOSE, а именно
Заключение
В данной статье было рассмотрено функциональное программирование на Perl, в частности, были рассмотрены такие темы — анонимные подпрограммы, замыкания и функции обратного вызова. Было проведено сравнение замыканий в Perl и классов в C++, функций обратного вызова (callbacks) в Perl и перегрузку функций-членов в C++. Также был расмотрен практический пример поиска битых ссылок с использованием AnyEvent::HTTP, в котором были использованы все вышеописанные возможности функционального программирования.