在这个脚本中,递归地扫描目录,我想知道当"ScanDirectory($name)"被调用时会发生什么->"下一个"得到执行之后?
因为如果@names在每次循环后被新目录填充,那么我们进入@names中的第一个目录,如果有其他目录,那么Scandirectory再次被调用,但之前@names中的其他目录被替换,因此它们不被循环处理?对不起,如果我不明白。
我知道已经有一个模块用于此目的,但我想提高我对这个循环代码如何工作的理解,以便我可以在其他情况下处理递归代码
sub ScanDirectory {
my $workdir = shift;
my $startdir = cwd;
chdir $workdir or die;
opendir my $DIR, '.' or die;
my @names = readdir $DIR or die;
closedir $DIR;
foreach my $name (@names) {
next if ($name eq ".");
next if ($name eq "..");
if (-d $name) {
ScanDirectory($name);
next;
}
}
chdir $startdir or die;
}
ScanDirectory('.');
这是你的代码吗?
在子例程中,您调用my @names = readdir
,它定义了一个新的词法作用域变量,因此每次递归都将创建该变量的新实例。如果你用our
代替my
,它可能会起作用。用our
定义的变量是打包作用域,这意味着每个调用将使用相同的@names
变量。其实那时也没有。您正在使用readdir
清除变量的前一个值。
你最好使用File::Find。File::Find
与大多数Perl安装一起提供,所以它总是可用的。
use strict;
use warnings;
use File::Find;
my @names;
find ( sub {
next if $_ eq "." or $_ eq "..";
push @names, $File::Find::name;
}, "."
);
这更容易理解,更容易编写,更灵活,更高效,因为它不递归地调用自己。大多数情况下,您将在函数中没有嵌入sub
的情况下看到这一点:
my @names;
find ( &wanted, ".");
sub wanted {
next if $_ eq "." or $_ eq "..";
push @names, $File::Find::name;
}
如果子程序相当小,我更喜欢嵌入子程序。它可以防止子例程偏离find
调用,并且可以防止在没有明确定义的情况下在子例程中使用@names
的神秘实例。
好的,它们都是一样的。两者都是子例程引用(一个称为wanted
,另一个是匿名子例程)。然而,@names
的第一次使用并不显得那么神秘,因为它实际上是在find
调用的正上方定义的。
如果必须从头编写自己的例程(可能是家庭作业?),那么不要使用递归。使用push
将反向的 readdir
压入数组。
然后,每次弹出一个数组项。如果找到一个目录,读入它(还是反向读入)并将其压入数组。注意.
和..
这是一段奇怪的代码,特别是如果它被发表在一本书中。
您的困惑是因为@names
数组在词法上被声明为,这意味着它仅存在于当前块的范围内,并且对于特定的堆栈帧(子例程调用)是唯一的。因此,每次调用scan_directory
(本地标识符不应该包含大写字母)都有自己独立的@names
数组,该数组在子程序退出时消失,并且不存在"替换"内容的问题。
而且,您所指的next
是冗余的:它跳过@names
数组的下一次迭代,这就是没有它会发生的情况。
这样写会好得多
sub scan_directory {
my ($workdir) = @_;
my $startdir = cwd;
chdir $workdir or die $!;
opendir my $dh, '.' or die $!;
while (my $name = readdir $dh) {
next if $name eq '.' or $name eq '..';
scan_directory($name) if -d $name;
}
chdir $startdir or die $!;
}
scan_directory('.');