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

Hi Monks

Me again ;-) Again thanks to all for their help on my syslog project I think I am now self sustaining and have a better understanding of Hashes etc. I do have one interesting bug probably a simple explanation for the guru's:

I have a simple hash but the last element will not print out unless I put "{why} on the end of it. If I don't put it on or take the "keys" of the last loop the print out just gives me back the hash ref =  A B HASH(0x22501c) . Can some one please tell me how to do this properly as I don't think code with bugs is safe code and I might end up with bug ridden results - here is my example:

#c:\perl\bin\perl my %line; $why = ""; $a = A; $b = B; $c = C; $line{$a}{$b}{$c}; $i = 1; for $a (keys %line) { for $b (keys %{$line{$a}}) { for $c (keys %{$line{$a}{$b}}) { print "$a $b $c\n"; } } }

Thanks very much

Pierre

Replies are listed 'Best First'.
Re: Hash code bug
by davorg (Chancellor) on Apr 28, 2006 at 08:21 UTC

    This is a case where adding "use warnings" to the code will explain what is going on.

    Useless use of hash elem in void context at ./hash line 9.

    Line 9 is:

    $line{$a}{$b}{$c};

    and that code isn't doing anything (Note: well, it isn't doing what you think it's doing - see reasonablekeith's reply below for details). What do you think it should do?

    You could also try printing the hash contents with Data::Dumper. You'll see that it's still empty and therefore your various calls to keys will all return empty lists.

    If you change the line to:

    $line{$a}{$b}{$c} = 1;

    then the hash has been given some data to hold and all of your keys spring into existance.

    --
    <http://dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

         and that code isn't doing anything. What do you think it should do?

      That's not quite true. Perl will autovivify the first two keys. Dumper reveals %line to be...

      print Dumper(\%line); __OUTPUT__ $VAR1 = { 'A' => { 'B' => {} } };
      ---
      my name's not Keith, and I'm not reasonable.
Re: Hash code bug
by reasonablekeith (Deacon) on Apr 28, 2006 at 09:10 UTC
    firstly Secode, don't use $a or $b, they are special variables used by Perl in sorting routines/

    secondly, also always

    use strict; use warnings;
    use strict forces you to declare any new variables with my (or similar), so you'll avoid silly typos like
    my $string = "why doesn't this print?"; print $strnig;
    thirdly, always quote your barewords (by default use strict forces to do this too). What I mean by this is do...
    my $string = "A"; # rather than my $string = A;
    If this seems petty, then look at the following.
    sub A { return 'B' }; # ... 1000 lines of complicated code ... my $string = A; print "$string\n"; # this prints "B"
    Without knowing that A is a subroutine you could be forgiven for think your output might be A. Save yourself, and the people who maintain your code, the effort and clear up this ambiguity and always "use strict"

    using warnings will give you handy hints, like when you use a variable but haven't defined a value for it yet. A lot of it's messages can be ignored, but they'll often give you hints to potential problems with your code.

    Applying all this to your script gives you...

    #/perl/usr/bin use warnings; use strict; my %line; my $why = ""; my $key_a = 'A'; my $key_b = 'B'; my $key_c = 'C'; $line{$key_a}{$key_b}{$key_c}; # this is line 12 my $i = 1; for my $loop_key_a (keys %line) { for my $loop_key_b (keys %{$line{$loop_key_a}}) { for my $loop_key_c (keys %{$line{$loop_key_a}{$loop_key_b}}) { print "$loop_key_a $loop_key_b $loop_key_c\n"; } } }
    Which, when run, issues the warning "Useless use of hash element in void context at rja.pl line 12.", which might have been enough of a clue for you to figure this one out for yourself.

    Happy Coding!

    ---
    my name's not Keith, and I'm not reasonable.
      A lot of [warnings] can be ignored

      In general, warnings should NOT be ignored. There are certain exceptions (e.g., in a CGI script it is common to interpolate an input into link URLs without checking whether it is defined, so that if the user once specifies it it is subsequently preserved), but even there you should only ignore these warnings if you are certain you understand fully what is causing them, and even then it's good practice to clean them up, to make life easier when maintaining your code later (since noisy output can obscure real issues).

      If you get into the habbit of ignoring warnings, you're going to end up with a big mess down the road.

Re: Hash code bug
by McDarren (Abbot) on Apr 28, 2006 at 09:17 UTC
    It's worth pointing out a couple of things here...
    1. Avoid using the scalar variables $a and $b. These are special global variables that are used by sort
    2. If you are new to Perl, you should always, always use strict and use warnings in every perl script that you write (at least until you know Perl well enough to be able to explain when it is safe or reasonable to not use them). This will save you many many hours of pulling your hair out because of silly typos and coding mistakes.

    Regards,
    Darren :)

Re: Hash code bug
by gube (Parson) on Apr 28, 2006 at 09:06 UTC

    Hi Secode

    If you need to get keys as output A B C you just use the below code. Always use Data::Dumper to see the complete stored hash format. I put the print statement in my code just refer that it will show the complete hash format. Your hash format is stored in this format
    Your Hash stored format: $VAR1 = { 'A' => { 'B' => {} } };
    This output is now stored..so, using this you can get the key output A + B C..so, assign some value for the key if you stored values in hash $VAR1 = { 'A' => { 'B' => { 'C' => 1 } } };
    #!/usr/bin/perl use Data::Dumper; my %line; $why = ""; $a = A; $b = B; $c = C; $line{$a}{$b}{$c} = 1; print Dumper(\%line); $i = 1; for $a (keys %line) { for $b (keys %{$line{$a}}) { for $c (keys %{$line{$a}{$b}}) { print "$a $b $c\n"; } } } <p>It prints output : A B C