Почему рекурсивный вызов функции не меняет поток выполнения? - PullRequest
2 голосов
/ 20 декабря 2010
sub dir_list1
{
    $path=$_[0];
    while(<$path/*>){
        if (-f "$_"){
            print "$path/$_\n";
        }
        else {
            print "dir: $path/$_\n";# if ($entry ne "." && $entry ne "..");
            dir_list1($_);
        }
    }
}
dir_list1(".");

Когда я выполняю вышеуказанный код, он сначала печатает ВСЕ содержимое текущего каталога, а затем переходит к списку содержимого подкаталога. Разве он не должен входить в подкаталог, когда встречает подкаталог, перечислить файлы внутри и продолжить с родительской папкой?

Спасибо.

[Изменить, в ответ на OrangeDog]

Я использую этот код в Windows. Вывод примерно такой:
a.txt
b.txt
dir: ./images
c.txt
d.txt
... [а затем папка с изображениями в списке]
./images/qwe.jpg
./images/asd.jpg
./images/zxc.jpg
...

Ответы [ 2 ]

4 голосов
/ 20 декабря 2010

У вас здесь куча проблем. Вот версия, которая действительно работает:

use strict;
use warnings;

sub dir_list1
{
    my $path = $_[0];
    for (<$path/*>) {
        if (-f $_) {
            print "$_\n";
        }
        else {
            print "enter dir: $_\n";
            dir_list1($_);
            print "leave dir: $_\n";
        }
    }
}
dir_list1(".");

Что не так с оригинальным кодом:

  • Отсутствие use strict; use warnings;
  • Не используется лексическая переменная для $path
  • Использование кавычек вокруг переменной обычно излишне ("$_")
  • Имена файлов, возвращаемые оператором glob, включают путь, который вы ему дали

Но фундаментальная проблема заключалась в том, что оператор glob в скалярном контексте нельзя использовать рекурсивно. Используемый итератор привязан к этой конкретной строке кода. Когда вы выполняете рекурсию, итератор все еще возвращает имена файлов из родительского каталога.

Я изменил ваш while (скалярный контекст) на for (список контекста). Цикл for генерирует полный список имен файлов, а затем перебирает его, и его можно использовать рекурсивно.

Я предполагаю, что вы делаете это как учебное упражнение. В противном случае вам следует использовать один из множества модулей для поиска файлов. Вот частичный список:

  • File :: Find - классический, базовый модуль с 5.000. Но раздражает интерфейс.
  • File :: Find :: Rule - упаковывает File :: Find в более приятный интерфейс
  • File :: Next - имеет основанный на итераторах интерфейс, который позволяет избежать необходимости читать все дерево каталогов перед возвратом чего-либо
  • Path :: Class :: Iterator - как File :: Next, но с волшебными именами объектов Path :: Class

Я уверен, что я упустил еще кое-что.

1 голос
/ 20 декабря 2010

(я не собирался публиковать это как ответ, но вы попросили исправленную версию кода в комментарии, так что… мое «исправление» будет полностью переписано как часть философии избегания повторного изобретения колеса) ,

Я бы справился с этим, используя модули CPAN, где это возможно. Это имеет приятные побочные эффекты, такие как «Выполнение правильных действий» с разделителями путей на разных платформах и сокращение числа строк в подпрограмме на 1/3.

#!/usr/bin/perl

use strict;
use warnings;
use v5.10;  # If you aren't using Perl 5.10 or newer, you should be. say alone makes tidier code
use Path::Class::Iterator; 

sub dir_list1 {
        my $path = shift;
        my $it = Path::Class::Iterator->new(root => $path,breadth_first => 0);
        until ($it->done) {
                my $f = $it->next;
                print 'dir: ' if $f->isa('Path::Class::Dir');
                say $f;
        }
}

dir_list1 "./";
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...