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

Hello Monks,

I have trouble with complex data structures and I'm currently wondering why the following code doesn't work:

use strict; use warnings; sub base_code (\%); my %aas = ( 'serine' => ['TCA', 'TCC', 'TCG', 'TCT'], 'proline' => ['CCA', 'CCC', 'CCG', 'CCT'] ); my %codes; $codes{'earth'} = \%aas; $codes{'mars'} = { 'serine' => ['QWZ', 'QWX', 'QWW'], 'proline' => ['ZXZ', 'ZXX', 'ZXQ', 'ZXW'] }; base_code (%codes); ## line 17 sub base_code (\%) { my $h_ref = $_[0]; foreach my $planet (keys %$h_ref){ foreach my $aa (keys %$planet){ + ## line 22 foreach my $codon (@{ $planet->{$aa} }){ print $codon, "\n"; } } } }

the error message is: Can't use string ("earth") as a HASH ref while "strict refs" in use at ex9.pl line 22.

Can anyone help? Thanks in advance :)

for extra credits: Why does it complain if I write base_code (\%codes) in line 17 ? I thought it must be a reference..

Replies are listed 'Best First'.
Re: printing complex data structures (values)
by LanX (Saint) on Jul 24, 2017 at 15:17 UTC
    Hint : it's much easier to traverse deep data structure when iterating over values instead of keys, especially if you never need the keys.

    Please note my naming convention using trailing h_ and a_ to denote hash refs and array refs.

    i.e. h_codes -> h_planet -> a_aa see included dump

    Like this it's obvious when to use @$a_ and %$h_ for dereferencing ...

    use strict; use warnings; use Data::Dump qw /pp dd/; my %aas = ( 'serine' => ['TCA', 'TCC', 'TCG', 'TCT'], 'proline' => ['CCA', 'CCC', 'CCG', 'CCT'] ); my %codes; $codes{'earth'} = \%aas; $codes{'mars'} = { 'serine' => ['QWZ', 'QWX', 'QWW'], 'proline' => ['ZXZ', 'ZXX', 'ZXQ', 'ZXW'] }; dd \%codes; # dump for clarity base_code (\%codes); sub base_code { my ($h_codes) = @_; for my $h_planet (values %$h_codes) { for my $a_aa (values %$h_planet) { # for my $codon ( @$a_aa ) { print $codon, "\n"; } } } }

    { earth => { proline => ["CCA", "CCC", "CCG", "CCT"], serine => ["TCA", "TCC", "TCG", "TCT"], }, mars => { proline => ["ZXZ", "ZXX", "ZXQ", "ZXW"], serine => ["QWZ", "QWX", "QWW"], }, } TCA TCC TCG TCT CCA CCC CCG CCT QWZ QWX QWW ZXZ ZXX ZXQ ZXW

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      Using values allows more direct access to nested hash values, but pay heed to haukex's update note here discussing the fact that keys allows control of the ordering of access to hash values, and this may be very important, in particular for large data structures. (See also perldsc.)


      Give a man a fish:  <%-{-{-{-<

        In this case it's IMHO better to use a hash-slice @hash{LIST}

        ... it'll give me full control over the order since I can include any LIST not just sorted keys.

        But still basically operating on values...

        sub base_code { my ($h_codes) = @_; for my $h_planet ( @$h_codes{ sort keys %$h_codes }) { for my $a_aa ( @$h_planet{ sort keys %$h_planet}) { for my $codon ( @$a_aa ) { print $codon, "\n"; } } } }

        or for convenience

        sub sorted_values { my $h_ref =shift; return @$h_ref{ sort keys %$h_ref } } sub base_code2 { my ($h_codes) = @_; for my $h_planet ( sorted_values $h_codes ) { for my $a_aa ( sorted_values $h_planet ) { for my $codon ( @$a_aa ) { print $codon, "\n"; } } } }

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

      ++ for using values() and eliminating all of the extraction. I use values() so infrequently I tend to forget it exists :)

      Also a good hint, thank you!!
Re: printing complex data structures
by stevieb (Canon) on Jul 24, 2017 at 15:04 UTC

    First, don't use prototypes unless you know why you need them. Also, in Perl, you do not need to pre-declare your subroutines like you do in C for instance.

    Now, you were trying to use $planet as a hash reference, but it's actually a string (which you extract when you call keys %$h_ref. So, you have to pass in a reference to a hash in the call to base_code(), then inside the sub, you need to use the key name $planet as the key to the encompassing $h_ref to extract out the data you want:

    use strict; use warnings; my %aas = ( 'serine' => ['TCA', 'TCC', 'TCG', 'TCT'], 'proline' => ['CCA', 'CCC', 'CCG', 'CCT'] ); my %codes; $codes{'earth'} = \%aas; $codes{'mars'} = { 'serine' => ['QWZ', 'QWX', 'QWW'], 'proline' => ['ZXZ', 'ZXX', 'ZXQ', 'ZXW'] }; base_code (\%codes); sub base_code { my $h_ref = $_[0]; for my $planet (keys %$h_ref){ for my $aa (keys %{ $h_ref->{$planet} }){ for my $codon (@{ $h_ref->{$planet}{$aa} }){ print $codon, "\n"; } } } }

    Output:

    CCA CCC CCG CCT TCA TCC TCG TCT ZXZ ZXX ZXQ ZXW QWZ QWX QWW
      you do not need to pre-declare your subroutines like you do in C for instance.

      Nitpick: You do in the case of the OP's code, because otherwise Perl will complain "main::base_code() called too early to check prototype" and the code won't work (because Perl doesn't know to take a reference to %codes as the argument). In general, if a sub has a prototype, Perl needs to see its declaration before it is called. But of course I agree with the advice to not use a prototype in the first place, in which case the issue becomes moot :-)

        Ah, yes, of course :) Thanks for pointing that out.

      thank you very much!
Re: printing complex data structures
by haukex (Archbishop) on Jul 24, 2017 at 15:07 UTC

    If you want to figure this out yourself, one thing that's helpful is to use e.g. Data::Dump to show the data structure. So for example, immediately after foreach my $planet (keys %$h_ref){, add dd $planet, $h_ref->{$planet}; to look at what you're dealing with at that point. You'll see that $planet is a string (the key of the hash), and that $h_ref->{$planet} (the value of the hash) is the hash reference that you should be dereferencing on the next line, i.e. foreach my $aa (keys %{ $h_ref->{$planet} }), and you have the same issue on the following line.

    Extra credit: Because you're using Prototypes in sub base_code (\%), when you write base_code (%codes);, that is actually being translated by Perl to &base_code(\%codes). My recommendation would be to not use prototypes (sub base_code {...) and instead call the sub as base_code(\%codes);. See my recent post Fun with Prototypes for a demonstration of one of the problems with prototypes.

    Update: Just a minor note: since hashes are unordered, keys will return the keys of the hash in an apparently random order. If you want your output to be consistent across different runs of the program, you could use foreach my ... (sort keys %...).

Re: printing complex data structures (prototypes)
by LanX (Saint) on Jul 24, 2017 at 15:06 UTC
    > Why does it complain if I write base_code (\%codes) in line 17 ? I thought it must be a reference..

    Because you are using a prototype (\%) which is only accepting %vars and delivering refs.

    Better don't use prototypes if you don't know how to handle them... or in other words if you are a beginner.

    see perlsub#Prototypes for details

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!