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

This is a strange one, but I've spent a whole day on it and I still can't figure it out.

I'm using the loop:

foreach my $article (keys %locations) { process_article(*WFH, \%{$locations{$article}}, [], \@all_chro +ms); }

Basically what it does is go over the keys of a hash of hashes and pass the hash associated with each key on to a sub-routine. It does this fine for all entries except the last one. The last one it passes as having no values (that is $article=''). I can't figure out why. I printed all the keys to the hash of hashes just before the last iteration to make sure they're all there and they are. Also, a few lines before this loop I call it similarly:

foreach my $article (keys %locations) { process_article(*WFH, \%{$locations{$article}}, \@{$lists{"$li +st"}}, \@all_kwords); }

And this works just fine. The only difference I can see between the two loops is that I'm passing a reference to a full array instead of an empty one.

Any ideas?

-----------------------------------

Any comments about coding style are welcome.

Replies are listed 'Best First'.
Re: Problem with a hash of hashes
by monktim (Friar) on Aug 08, 2003 at 03:56 UTC
    Here is a bare bones example of printing a hash of hases with your logic. Maybe you can build around this.
    use strict; use warnings; my %locations = ( USA => { NY => '212', NJ => '973' }, ENG => { LDN => '44', CVT => '20' } ); foreach (keys %locations) { process_article(\%{$locations{$_}}); } sub process_article { my $hash = shift @_; print "$_ $hash->{$_}\n" foreach (keys %$hash); }
Re: Problem with a hash of hashes
by graff (Chancellor) on Aug 08, 2003 at 03:07 UTC
    It would help if you could provide enough of the code from the "process_article" function to let us know how the args are being treated. For example, since you are passing references, it's quite possible that some of the contents of your hash are being altered within the subroutine in ways you don't intend.

    In any case, as to style, if  $locations{$article} is a reference to a hash, then you should just pass this value to the subroutine -- you don't need to dereference it and put a backslash in front of that to create a reference again. Just do this:

    for my $article ( keys %locations ) { process_article( *WFH, $locations{$article}, ... ); }

      Here is process_article:

      sub process_article { my $fh=shift; # Filehandle my $locations=shift; # Reference to hash containing location informati +on for this article my $keywords=shift; # Reference to an array containing keywords my $all=shift; # Reference to a list of all keywords or chromosomal ba +nds found if (-e "$$locations{file}") { # If file exists in the location indicat +ed by the index file open(INFO, "$$locations{file}") or die("cannot open file $$locatio +ns{file}: $!"); } else { # If not, search for it my ($file)=($$locations{file}=~/.+\/(.+)/); # Get just the filenam +e find(sub{m/$file/ and $file=$File::Find::name}, 'c:/perl' ); open(INFO, "$file") or die("cannot open file $file: $!"); } seek (INFO, $$locations{offset}, 0); # Set location to article my $string=<INFO>; # Get line from file my ($title, $line, $abstract)=($string=~/(.+?)\t(.+)\t(.*)/); if (@$keywords) { # If keywords were provided my ($score, @found)=find_keywords($title.' '.$abstract, @$keywords +); # Get keywords push @$all, @found; my $kwords_found=join('|', @found); print $fh "$title\t$line\t$kwords_found\t$score\n" if ($score>0); +# Print line } else { # No keywords provided, means look for chromosomal bands my ($score, @found)=find_bands($title.' '.$abstract); # Get chromo +somes push @$all, @found; my $chroms_found=join('|', @found); print $fh "$title\t$line\t$chroms_found\t$score\n" if ($score>0); +# Print line } close(INFO); }

      -----------------------------------

      Any comments about coding style are welcome.
        OK, it looks like I may have guessed wrong about that -- sorry. (I don't see anything there that would cause the problem you described, that an element in the "%locations" hash of your calling routine seems to have an empty string as the hash key.

        So when you run the first loop, with a reference to a non-empty array of keywords to search for, it works fine and you don't see any evidence of a bogus entry in the "%locations" hash. Then, when you do a second loop, with a reference to an empty array instead of a keyword list, you see the extra "$article" key that turns out to be an empty string instead of a hash ref.

        Since it looks to me like the "process_article" sub is not changing anything in %locations, the next question would be: what happens between these two loops? Also, when you say that you print out all the keys in %locations "to make sure they're all there and they are", you should be looking for a key that is an empty string. To make this visible, you'd need to do something like:

        print ":",join(":", keys %locations),":\n";
        (If your keys include colon characters, use something else as a separator, e.g. "|".) If this print-out shows two separators in a row, you're looking at a hash element with an empty string as the hash key. In that case, you need to figure out where (and how many times) you are using an empty string to set or inspect an element of this hash array, before going into that second loop.

        It may also be possible, the way the "process_article" logic is written, that you would only notice an empty string for $article when you happen to be doing a "chromosome search"; i.e. the empty hash key is there in both loops, and only causes trouble in the second one. But I'm just guessing about that.

Re: Problem with a hash of hashes
by Zaxo (Archbishop) on Aug 08, 2003 at 03:18 UTC

    Also, that loop could be written,

    for (values %locations) { process_article(*WFH, $_, [], \@all_chroms); }

    After Compline,
    Zaxo

Re: Problem with a hash of hashes
by bean (Monk) on Aug 08, 2003 at 07:20 UTC
    Maybe I'm way off base here, but it seems to me that you're passing a reference to a full array in the second case and an empty anonymous array (an actual array, not a reference to one) in the first case. So in the first case, @$keywords would stringify the empty array, then make an array out of that empty string???
Re: Problem with a hash of hashes
by Skeeve (Parson) on Aug 08, 2003 at 07:29 UTC
    I think, bean already got it right.

    I have another question. Why do you create an anonymous hash from your hashreference with \%{$locations{$article}} and make areference of it instead of passing the actual reference with $locations{$article}? You don't change the hash in your sub AFAICS, so there is no need to copy the whole hash.