为什么递归函数调用不会改变执行流程?

时间:2010-12-20 09:52:50

标签: perl

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
导演:./ images周四 c.txt
d.txt
... [然后列出图像文件夹]
./images/qwe.jpg
./images/asd.jpg
./images/zxc.jpg
...

2 个答案:

答案 0 :(得分:4)

你在这里遇到了很多问题。这是一个实际可行的版本:

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 :(得分:1)

(我不打算将此作为答案发布,但是您要求在评论中更正版本的代码,所以...我的'更正'将是批发重写,作为避免轮子重新发明的哲学的一部分) 。

我会尽可能使用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 "./";