The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Обработка иерархически связанной структуры на Perl на perl.
Как-то пришлось столкнуться с обработкой иерархически связанной структуры на 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--;
	}
}
 
19.12.2005 , Автор: Andrey Karavaev
Ключи: perl, hash / Лицензия: CC-BY
Раздел:    Корень / Программисту и web-разработчику / Perl / Полезные подпрограммы на Perl / Работа с сетью и IP адресами на Perl

Обсуждение [ RSS ]
  • 1, Критик (?), 15:28, 29/12/2005 [ответить]  
  • +/
    По поводу "эффективность".

    Есть более красивое решение без рекурсии.

    Подсказка: берем массив, загоняем первый элемент и поехали. В цикле while(shift <наш массив>). Новые каталоги загоняем (push unshift) в массив. И никакой рекурсии.

     
     
  • 2, Andrey Karavaev (?), 21:56, 30/12/2005 [^] [^^] [^^^] [ответить]  
  • +/
    Более того - есть и более эффективные.
    И без всяких подсказок ;).
    Моя первоначальная задача была совсем не про
    директории и не про вывод на консоль :).
    Пришлось сильно упростить не теряя сути полезного
    алгоритма.
     

     Добавить комментарий
    Имя:
    E-Mail:
    Заголовок:
    Текст:




    Партнёры:
    PostgresPro
    Inferno Solutions
    Hosting by Hoster.ru
    Хостинг:

    Закладки на сайте
    Проследить за страницей
    Created 1996-2024 by Maxim Chirkov
    Добавить, Поддержать, Вебмастеру