resistance has asked for the wisdom of the Perl Monks concerning the following question:

Hi perlmonks!

I am doing a traversing of filesystem and try to autovivificate the hash. The problem - I don't know how to insert multiple keys in hash:

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; my %hash; my $a="{5}{6}"; # path to insert # trying to get $hash{root}{1}{5}{6}{2}="b"; $hash{root}{1}{$a}{2}="b"; print Dumper(%hash);

but I get $hash{root}{1}{'{5}{6}'}{2}="b". How to get $a interpolated as part of the "path" instead of single key?

Thanks!

Replies are listed 'Best First'.
Re: Case of autovivification
by ikegami (Patriarch) on Oct 11, 2008 at 15:32 UTC

    It would be really bad if Perl started treating the contents of variables as Perl code. Use Data::Diver.

    #!/usr/bin/perl use strict; use warnings; use Data::Diver qw( DiveVal ); use Data::Dumper qw( Dumper ); my %hash; my @keys = ( 5, 6 ); # $hash{root}...{2} = 'b'; DiveVal( \%hash, map \$_, 'root', @keys, 2 ) = 'b'; print Dumper(\%hash);

    Tip: For best results, always pass a scalar to Dumper. If you want to dump an array or hash, pass a reference to it.

Re: Case of autovivification
by JavaFan (Canon) on Oct 11, 2008 at 16:27 UTC
    It's easier if you store the keys in an array. For instance:
    my %hash; my @path = (5, 6); my $ref = $hash{root}{1} ||= {}; $ref = $$ref{$_} ||= {} for @path; $$ref{2} = "b"; use YAML; print Dump \%hash; __END__ --- root: 1: 5: 6: 2: b

      Sorry, I wrote a bad example, my mistake. Please look at the complete code, I am trying to build hash of hashes where each folder is a key. Early I did hash of hashes where keys are full path to folder, but it wasn't pretty.

      #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my %filesystem; my $ref = $filesystem{root} ||= {}; traverse(".",""); sub traverse{ my ($folder,@path) = @_; opendir DIR,$folder; my @list = grep {!/^\.{1,2}$/} readdir DIR; # exclude . and .. closedir DIR; foreach my $entry(@list){ next if ( -l $folder."/". $entry); # skip links $ref = $$ref{$_} ||= {} for @path; if( -d $folder."/". $entry){ traverse($folder."/".$entry,@path,$entry); }else{ $$ref{$folder} = $entry; }; }; }; print Dumper(\%filesystem);

      if I run it it eat all my RAM, so I think I use $ref from your example wrong

        It seems like you are trying to build up a deeply nested data structure which looks a lot like your directory tree. However, if all you need is a simple array of full paths to files, take a look at File::Find::Rule, which recursively traverses the filesystem.
        You need to decide what you want your data structure to look like before you do anything else. Right now, you're trying to store every non-directory file in directory at the same key as the directory itself.

        Try running your script with some extra print statements and you can see what it is doing as it progresses. I expect the data structure it is building is not what you want and may help explain why you are using so much memory.

        You should think carefully about the line $ref = $$ref{$_} ||= {} for @path; in your subroutine.

        Also, your subroutine is recursive. It keeps changing the global variable $ref. Think about what $ref is referencing and how this changes as your program progresses.