Как-то пришлось столкнуться с обработкой иерархически связанной структуры на perl.
В инете есть куча разрозненной информации по этому поводу.
Можно, например, воспользоваться пакетами с сайта CPAN. Но с одной
стороны
стрелять из пушки по воробьям ... не дело.. а с другой надо чтобы и в мозгах что-то осталось.
Вообщем, задачка решилась и заодно родился вот такой скриптик, не претендующий
на уникальность, тем более, что на perl (как и на других мощных языках) одну и ту же задачу
можно решить многими способами. Хотя, эффективность этих способов - это уже другой вопрос.Итак, скрипт.
#!/usr/bin/perl
# Рассмотрим принцип работы рекурсивных функций и построения
# связанных структур (в данном случае анонимных хэшей) на
# примере скрипта для иерархического (в виде дерева)
# отображения подкаталогов, содержашихся в заданном каталоге.
#
# Сначала необходимо провернуть некоторые подготовительные
# операции. Например, определиться какой каталог будем печатать.
print "Directory to print [.]: ";
# Считываем каталог и удаляем символ конца строки.
chop (my $d = <>);
# По умолчанию берем текущий каталог.
if (!$d) {$d="."};
# Проверяем, является ли $d каталогом...?
(-d $d) or die "Error: $d isn\'t directory.\n";# Теперь создаем дерево вложенных хэшей с помощью
# функции MakeTree, которая возвращает указатель
# на корневой хэш.
my $root = MakeTree($d);
# И печатаем. Первый параметр - уровень вложенности
# текущего каталога - $d.
PrintTree(0,$root);# Рассмотрим подробнее рекурсивные функции MakeTree и PrintTree
sub MakeTree {
# Берем первый параметр - каталог для обработки
my $path_to_dir = shift;
# Инициализируем хэш, в котором будем сохранять
# результат обработки каталога $path_to_dir.
my %branches;
# Читаем содержимое каталога в массив @content.
# Причем выбрасываем из рассмотрения каталоги
# "." (текущий), ".." (уровнем выше) и файлы.
opendir DIR, $path_to_dir;
my @content = grep { !/^\.{1,2}$/
&& !(-f $path_to_dir."/".$_) } readdir DIR;
closedir DIR;
# В итоге в @content содержится список каталогов,
# находящихся в $path_to_dir. Теперь для каждого
foreach my $dir (@content) {
# каталога из этого списка рекурсивно запускаем
# эту же функцию - MakeTree, в аргументе которой
# уже новый путь - путь к каталогу $dir.
$branches{$dir} = MakeTree($path_to_dir."/".$dir);
# В результате в хэш %branches по ключу $dir заносится
# значение, возвращаемое функцией MakeTree. А это значение
}
# ничто иное, как указатель на хэш, поскольку функция
# MakeTree возврашает указатель на хэш %branches, как мы
# видим ниже. Ключи этого хэша - каталоги, содержащиеся
# в $dir, а значения по этим ключам - опять же указатели на
#хэши...и т.д.
return \%branches;
# Но ведь %branches обьявлен с ключевым словом my и ограничен
# областью видимости функции (в данном случае)?!..Это означает,
# что сборщик мусора perl должен уничтожить %branches при
# выходе из функции. Оказывается, нет. Дело в том, что пока для
# переменной (в данном случае - хэш), объявленной с оператором
# my внутри блока { } существует указатель вне этого блока, то
# perl не уничтожает переменную, и мы приходим к понятию
# анонимная переменная (хэш). Т.е. мы обращаемся к переменной
# не $имя_переменной, а через указатель.
}
# Таким образом, в результате создаются ссылающиеся друг
# на друга анонимные хэши, которые существуют до тех пор,
# пока существует указатель $root.# Печатаем результат.
sub PrintTree {
# Первый параметр - уровень вложенности каталога.
my $level = shift;
# Второй - хэш, содержащий имена каталогов, через указатель.
my %top = %{(shift)};
# Далее для каждого ключа из хэша %top
foreach $key (keys %top) {
# печатаем сам ключ (на самом деле ключ - имя каталога),
# причем с отступом 4*$level.
printf ("%${\(4*$level)}s$key\n","|");
# Необходимо заметить, что в %top по ключу $key содержится
# хэш с именами каталогов, которые тоже было бы неплохо
# распечатать. Поэтому увеличиваем уровень вложенности
$level++;
# и опять вызываем PrintTree.
PrintTree($level,$top{$key});
# После чего надо вернуть уровень вложенности на место.
$level--;
}
}
URL:
Обсуждается: http://www.opennet.me/tips/info/947.shtml
По поводу "эффективность".Есть более красивое решение без рекурсии.
Подсказка: берем массив, загоняем первый элемент и поехали. В цикле while(shift <наш массив>). Новые каталоги загоняем (push unshift) в массив. И никакой рекурсии.
Более того - есть и более эффективные.
И без всяких подсказок ;).
Моя первоначальная задача была совсем не про
директории и не про вывод на консоль :).
Пришлось сильно упростить не теряя сути полезного
алгоритма.