【问题标题】:Why doesn't the recursive function call not change the execution flow?为什么递归函数调用不会改变执行流程?
【发布时间】:2010-12-20 09:52:49
【问题描述】:
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 上使用此代码。输出是这样的:
一个.txt
b.txt
目录:./images
c.txt
d.txt
... [然后列出图像文件夹]
./images/qwe.jpg
./images/asd.jpg
./images/zxc.jpg
...

【问题讨论】:

  • 你怎么知道你的文件系统不只是返回列表末尾的目录?
  • @David:我将研究函数在 Perl 中的工作方式。我想那是我出错的地方。您能否发布此代码的更正版本供我查看?
  • 这与函数无关,只是全局。
  • 阅读 David 的评论,并将其铭刻在你的脑海中,除非你正在编写一个快速肮脏的“凌晨 2 点”脚本,使用 strictwarnings(和 diagnostics 可选)是绝对必要的! :)
  • 我想说这甚至适用于快速而肮脏的凌晨 2 点脚本:)

标签: perl


【解决方案1】:

这里有很多问题。这是一个实际有效的版本:

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 的神奇文件名对象

我确定还有更多我忽略了。

【讨论】:

    【解决方案2】:

    (我不打算将此作为答案发布,但您在评论中要求提供代码的更正版本,所以……我的“更正”将是作为避免轮子改造理念的一部分的大规模重写) .

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

    【讨论】:

      猜你喜欢
      • 2016-02-04
      • 2015-03-02
      • 2018-03-11
      • 1970-01-01
      • 2021-09-26
      • 2019-08-29
      • 1970-01-01
      • 2013-10-29
      • 1970-01-01
      相关资源
      最近更新 更多