Разработка

Работу над программой начнём объявления ассоциативного массива %collectedFiles и с копирования определения процедуры compareFiles из нашей программы cmp.pl:

Perl
my %collectedFiles; sub compareFiles($$;$) { }

Объявление и определение процедуры find мы тоже позаимствуем из программы find.pl, но с небольшими изменениями:

Perl
sub find($); sub find($) { my $name=shift; if(-d $name) { (удаляем print "$name\n";) } elsif(-f $name) { push @{$collectedFiles{-s $name}->[0]}, $name; (вместо print "$name\n";) } else { } }

Смысл этих изменений заключается в следующем. Поскольку никакая обработка найденных директорий нам не требуется (мы собираемся сравнивать лишь файлы), мы удалили первую команду print. Файлам же нужна особая обработка. Расшифруем не совсем очевидную строчку, начинающуюся с push. Выражение $collectedFiles{-s $name} — это гнездо файлов размера -s $name, то есть список групп (ссылка на анонимный массив). В этом списке пока лишь одна группа, $collectedFiles{-s $name}->[0] (она тоже является ссылкой на анонимный массив). Выражение @{$collectedFiles{-s $name}->[0]} даёт сам массив, на который указывает ссылка. И в него-то мы и добавляем имя файла с помощью push.

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

Perl
findDups(@ARGV);

Займёмся её программированием:

Perl
sub findDups(@) { push @_, '.' unless @_; работаем по умолчанию с текущей директорией find($_) for @_; заполняем %collectedFiles for my $size(sort { $a<=>$b } keys %collectedFiles) перебираем всевозможные размеры файлов (по возрастанию) — это ключи в %collectedFiles { my $groups=$collectedFiles{$size}; получаем список групп для данного размера for(my $g=0; $g<@$groups; $g++) перебираем номера групп в списке { my $group=$groups->[$g]; берём группу с номером $g и обрабатываем её: for(my $i=1; $i<@$group; $i++) { push @{$groups->[$g+1]}, splice @$group, $i--, 1 if compareFiles($group->[0], $group->[$i]); } if(@$group>1) { print "* Размер: $size\n"; print "$_\n" for sort @$group; print "\n"; } } } }

Обработка группы номер $g заключается в переборе всех её элементов, начиная со второго, сравнении их с первым, и, если обнаружено различие (if compareFiles($group->[0], $group->[$i])), в переброске такого элемента в следующую, $g+1-ю группу. Разберём операцию переброски подробнее. Выражение splice @$group, $i--, 1 удаляет $i-й элемент из группы. Поскольку на место удалённого элемента встанет следующий (если он есть), необходимо уменьшить счётчик элементов $i, чтобы компенсировать его автоматическое увеличение в цикле; без этого элемент, следующий после удалённого, будет пропущен без сравнения с первым. Выражение splice возвращает удалённое имя файла, которое тут же добавляется в $g+1-ю группу командой push @{$groups->[$g+1]}, …. Затем группы, включающие более одного имени файла, выводятся на экран с указанием размера файлов в группе.

Информатика-54© А. Н. Швец