Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

How to correctly use a symbolic reference to automatically generate hash names?

by lightoverhead (Pilgrim)
on Mar 04, 2014 at 21:16 UTC ( [id://1076958]=perlquestion: print w/replies, xml ) Need Help??

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

Monks, The code is as this. I need to automatically generate hash names and push some element to an array which is the value of the hash.But I just cannot get it right.
for (my $i = 0; $i< scalar @files; $i++) { open FILE,"<",$files[$i]; my $hashname = "wow".$i; while(<FILE>){ chomp; my @row = split(/\t/,$_); push @{${$hashname}{$row[1]}}, "sth"; #really wanted is push @ +{$wow1{$row[1]}},"sth" if hash used is "%wow1" when $i==1 } close FILE; }

Replies are listed 'Best First'.
Re: How to correctly use a symbolic reference to automatically generate hash names?
by AnomalousMonk (Archbishop) on Mar 04, 2014 at 21:54 UTC
Re: How to correctly use a symbolic reference to automatically generate hash names?
by runrig (Abbot) on Mar 04, 2014 at 21:30 UTC
    No. Have one hasharray of lists of lists hashes (NOT multiple hashes), and do, e.g.:
    my @wow; ... push @{$wow[$i]{$row[$i]}}, "sth";

      Thank you for your reply.

      However, what I have is a set of hashes such as %wow0, %wow1,%wow2, %wow3, .... That's why I want to use a loop to handle these hashes and I need an automatic way to refer to these hashes and to update info on these hashes.

        Why on earth would you have %wow1, %wow2, and %wow3, instead of %{$wow[1]}, %{$wow[2]}, and %{$wow[3]}? In other words, instead of:

        %wow1 = ( key => value, key => value ); %wow2 = ( key => value, key => value );

        You could have...

        @wow = ( { key => value, key => value }, { key => value, key => value }, );

        Which is an array of hashes, instead of a bunch of hashes loosely related by naming convention.

        I see no advantage to polluting your package global namespace with variable names that are tricky to manipulate when you could just create a single AoH structure.


        Dave

        I am suggesting to not have a set of hashes named %wow0, %wow1, etc., and to instead do as I have suggested.
Re: How to correctly use a symbolic reference to automatically generate hash names?
by kcott (Archbishop) on Mar 04, 2014 at 23:14 UTC

    G'day lightoverhead,

    As has already been pointed out, using symbolic references this way is a bad idea. If you're interested purely for academic reasons, see "perlref: Symbolic references"; understanding the refs stricture of the strict pragma would also be useful.

    As an alternative, consider the technique in this script (and see the Notes at the end):

    #!/usr/bin/env perl use strict; use warnings; use Inline::Files; my %master_hash; my @filehandles = (\*FILE1, \*FILE2, \*FILE3); for (0 .. $#filehandles) { my $fh = $filehandles[$_]; my $wow_key = "wow$_"; while (<$fh>) { chomp; push @{$master_hash{$wow_key}{(split /\t/)[1]}}, 'sth'; } } use Data::Dump; dd \%master_hash; __FILE1__ x file1line1 x file1line2 __FILE2__ x file2line1 x file2line2 __FILE3__ x file3line1 x file3line2

    Output:

    { wow0 => { file1line1 => ["sth"], file1line2 => ["sth"] }, wow1 => { file2line1 => ["sth"], file2line2 => ["sth"] }, wow2 => { file3line1 => ["sth"], file3line2 => ["sth"] }, }

    Notes

    • I've kept much the same logic as you show in your OP.
    • The wowN names you wanted are created and the hashes they refer to are easily accessible via a $master_hash{wowN} variable.
    • I've had to guess at input data as you didn't show any. For demo purposes, I've used Inline::Files; for your real code, continue to use an array of filenames and open.

    -- Ken

Re: How to correctly use a symbolic reference to automatically generate hash names?
by Anonymous Monk on Mar 04, 2014 at 21:57 UTC

    As far as I can tell, the code you posted should create hashes named %wow0, %wow1, etc. Please add to your question some sample input and its expected output, otherwise we can't know what you expect your code to output. Please see How do I post a question effectively?

    Why do you need these individual hashes, and why aren't you using a hash of hashes? Because if you can't answer those questions, the recommended way is using a hash of hashes. Among several reasons why, your current code won't run under use strict.

    Also, if you haven't yet, take a look at Data::Dumper to look at the data structures you are creating.

Re: How to correctly use a symbolic reference to automatically generate hash names?
by smls (Friar) on Mar 04, 2014 at 21:47 UTC

    The code you listed does in fact work, provided that you don't have "use strict;" in you your program - so if it does not do what you want, the problem may be with the input data or with other parts of your program.

    For example if @files is defined as...

    my @files = ("foo.txt");

    And the contents of file foo.txt are (PS: that whitespace is supposed to be tabs)...

    aaa bbb ccc 000 111 222

    ...then at the end of your code, the hash %wow0 will contain:

    ( "111" => ["sth"], "bbb" => ["sth"] )

    However, using symbolic references for that is most likely a bad idea - they tend to make your program fragile with regards to unexpected input, and make it easy to introduce annoying bugs. That's why "use strict;" disables symbolic references.

    As runrig already mentioned, you should consider using a data structure that does not rely on symbolic references, like a single nested hash or array that contains what you currently try to store in multiple %wow0, %wow1, ... hashes.

    For example:

    use warnings; use strict; my @files = ("foo.txt", "bar.txt"); my @wow; foreach my $i (0..$#files) { open my $fh, "<", $files[$i]; while(<$fh>){ chomp; my @row = split /\t/; push @{$wow[$i]->{$row[1]}}, "sth"; } close $fh; }

    Now $wow[0] (i.e. the first element of the array @wow) will contain a reference to an anonymous hash that corresponds to what you wanted to store in %wow0, and $wow[1] corresponds to your %wow1, etc., so that the contents of @wow will look like:

    ( { "111" => ["sth"], # data from foo.txt "bbb" => ["sth"] }, { ... } # data from bar.txt )

    Of course that's just one possibility. What data structure is best, depends on what exactly you're trying to do with that code. If you give more info (ideally including some sample input & expected output), we can help you better.

Re: How to correctly use a symbolic reference to automatically generate hash names?
by thezip (Vicar) on Mar 04, 2014 at 21:41 UTC

    I'm gonna have to see what your input and output should look like, because I'm not convinced that you have the right data structure in mind.

    From a 50,000 foot level, what problem are you trying to solve?


    *My* tenacity goes to eleven...
Re: How to correctly use a symbolic reference to automatically generate hash names?
by dsheroh (Monsignor) on Mar 05, 2014 at 10:33 UTC
    The code you posted should probably work, provided that use strict 'refs' is not in effect.

    This is because you're trying to use symbolic references (Perl's term for "a variable that contains the name of another variable") and using symbolic references is one of the things that strict is specifically intended to prohibit.

    This is because symbolic references are a bad idea in any circumstance where you might need to someday debug or maintain the code.

    Any time that you find yourself making a series of variables named var1, var2, var3, etc., that's a pretty sure sign that you really want an array rather than a list of variables with similar names. This applies just as much to hashes and arrays as it does to scalars.

Re: How to correctly use a symbolic reference to automatically generate hash names?
by Anonymous Monk on Mar 05, 2014 at 13:21 UTC
    It is almost always a bad idea to use phony-names in this way, especially when Perl's "auto-vivification" feature (Google it ...) makes it so darn easy to build "hashes of arrays of arrays of hashes of ..." Surely you want something like:

    $myhash->{'wow'}->[3]->{'bygolly'}

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1076958]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (4)
As of 2024-03-29 04:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found