Building a tree of files

Paul Johnson paul at
Wed Mar 1 23:22:57 GMT 2006

On Wed, Mar 01, 2006 at 06:06:13PM +0000, Andy Wardley wrote:

> I'm building HTML pages for a set of modules.  I want a nice nested
> menu which I can easily generate via TT given the appropriate data 
> structure.

> I've got my own hacked-up code built from File::Find::Rule which
> nearly works, but it's getting messy and I'm sure I'm missing
> some nice elegant solution.  So before I hack it around to
> make/break it better/worse I thought I'd ask to see if someone
> else has any pointers.

Empirical evidence suggests that I would probably write something
similar to that reproduced below.

I find this interesting since it shows again, at once, both the power of
Perl and the poor interface of File::Find.  I'm not sure whether
File::Find::Rule would be able to offer much of an improvement.  The
problem is crying out for a recursive solution.

The code below is a little fragile.  In particular the depth subroutine
should probably be improved.  The code is also completely uncommented
since I understand it completely and can't imagine how anyone else
couldn't.  It's also untested because, well, you can just see that it
works, can't you?

Oh well, the idea is that File::Find and the preprocess subroutine
provide us with a list of appropriate files and directories in the
correct order, and the wanted subroutine creates the data structure by
adding to the part of the structure stored in the stack at the
appropriate depth.  I suppose that's not really a stack then, although
it still sort of is, but naming variables is always the hardest part of
programming anyway.  That must be why Haskell is such an easy language.

Hmmm.  It's late, I have a cold, and I'm rambling.

And I really should watch the Blues Brothers again.


use strict;
use warnings;

use File::Find;

my $file      = qr/(.*)\.pm$/;
my $root      = shift || ".";
my $rootdepth = depth($root);
my @stack     = ([]);

find( { wanted => \&wanted, preprocess => \&preprocess }, $root);

sub depth { $_[0] =~ y[/][] }

sub preprocess
    map  $_->[0],
    sort { $a->[1] cmp $b->[1] or $a->[2] <=> $b->[2] }
    map  [ $_, $_ =~ $file ? $1 : $_, -f ],
    grep { $_ =~ $file || -d }

sub wanted
    my $depth = depth($File::Find::name) - $rootdepth - 1;
    push @{$stack[$depth]},
             name => $_,
             path => $File::Find::name,
             (-f) ? ( type => "file" )
                  : ( type => "directory", tree => $stack[$depth + 1] = [] )

use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Indent   = 1;
print Dumper $stack[0];

Paul Johnson - paul at

More information about the mailing list