Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

variable as hash name

by blackzero (Acolyte)
on Dec 08, 2012 at 04:19 UTC ( [id://1007852]=perlquestion: print w/replies, xml ) Need Help??

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

Hi. I need to create a hash wich name is take from a variable value in a code similar to this:

#!/usr/bin/perl use strict; use warnings; our $variable = 'name01'; { no strict 'refs'; our %$variable = ( valorC => 'value03', valorD => 'value04', ); use strict 'refs'; } print %$variable, "\n";

But %$variable gives an error (Can't declare hash dereference in "our" at ..., near "$variable =")

How should I do to make it work?

PLEASE, dont't tell me I dont want to do this.

I want to do exactly what I said above. Not to use hash of hashes or anything else. I DO want to use a variable as a hash name and I'm aware of the risks.

Thanks for any help.

Replies are listed 'Best First'.
Re: variable as hash name
by davido (Cardinal) on Dec 08, 2012 at 04:36 UTC

    After you fix the error message you're getting by removing "our" from line 9 (our %$variable ... just won't work), then you can move onto the strictures violation on line 13: print %$variable is still a strict violation even if you've already created the variable. If your code is going to be full of those constructs, you're going to have to run without strict 'refs', or jump through little "no strict 'refs'" contortions all over the place.

    You say this is something you really want to do. So do it. Just don't ask "strict" to condone it.

    use strict; use warnings; our $variable = 'name01'; no strict 'refs'; %$variable = ( valorC => 'value03', valorD => 'value04', ); print %$variable, "\n";

    I'm interested in hearing why your code might fit into the 0.5% of code that needs to implement this powerful and dangerous feature. Are you writing an exporter? What is the benefit? Is it worth it? Are you sure it's not a problem that could be solved by another level of indirection? You might just need a 3rd floor, not a 2nd basement.


    Dave

      Thank you, Dave. Now, the point is that I'm reading an external file in the following format:

      230;238;101;103;138;146;112;116;; 230;238;101;103;146;146;108;112;; 224;238;0;0;146;146;110;118;; 238;238;0;0;146;146;112;114;;

      I have an array named @loci_codes with this content:

      @loci_codes = qw( Bet01 Bet05 Bet06 Bet12 );

      What I want is to read each line ( <> ), split and put the elements in an array

      while ( defined( $line = <> ) ) my @alelos = split ";", $line;

      Than I want to take the elements of @alelos two by two and put each pair in a different hash. The PROBLEM is that I want each of these hashes to be named after on of the elements of @loci_codes.

      while ( @loci_codes ) { $variable = shift @loci_codes; %$variable; $allele1 = shift @alelos; $allele2 = shift @alelos; %$variable{$allele1) += 1; %$variable{$allele2} += 1; }

      And doing that for each line of the input, I want to have a hash named after a element of @loci_codes and populated with these pairs.

      EXAMPLE:

      %Bet01 = ( 230 => 2, 238 => 5, 224 => 1,); %Bet05 = ( 101 => 2, 103 => 2, 0 => 4, ); ...

      NOTE: The content of @loci_codes may change according to the input file readed. THATS why I cant use static names for the hashes.

        so how is that superior to using an explicit "name" level in your hash? Using a variable as a name simply introduces exactly the same effect as adding a "name" level to a hash, but you have no easy way to debug your code. If you use a conventional hash you can use modules like Data::Dump (or better still, use an IDE or the debugger) to inspect the contents of the hash for debugging purposes. Consider:

        #!/usr/bin/perl use strict; use warnings; use Data::Dump; my @loci_codes = qw( Bet01 Bet05 Bet06 Bet12 ); my %data; for my $dataIn ( qw( 230;238;101;103;138;146;112;116;; 230;238;101;103;146;146;108;112;; 224;238;0;0;146;146;110;118;; 238;238;0;0;146;146;112;114;; ) ) { my @alelos = split ";", $dataIn; for my $code (@loci_codes) { ++$data{$code}{shift @alelos}; ++$data{$code}{shift @alelos}; } } Data::Dump::dump(\%data); for my $code (@loci_codes) { print "$code: "; print join ', ', map {"$_ ($data{$code}{$_})"} sort {$a <=> $b} keys %{$data{$c +ode}}; print "\n"; }

        prints:

        { Bet01 => { 224 => 1, 230 => 2, 238 => 5 }, Bet05 => { "0" => 4, "101" => 2, "103" => 2 }, Bet06 => { 138 => 1, 146 => 7 }, Bet12 => { 108 => 1, 110 => 1, 112 => 3, 114 => 1, 116 => 1, 118 => +1 }, } Bet01: 224 (1), 230 (2), 238 (5) Bet05: 0 (4), 101 (2), 103 (2) Bet06: 138 (1), 146 (7) Bet12: 108 (1), 110 (1), 112 (3), 114 (1), 116 (1), 118 (1)
        True laziness is hard work

        I was hoping that you might come up with some interesting and compelling reason -- some problem that can only be solved by getting your hands dirty in the muck of the symbol table. Instead, I'm seeing a description of a problem that can be solved with one more layer of hard references; a hash of hashes, for example. That, you can read about in perldsc. GrandFather is right; you just need a higher level layer of abstraction; a hash of hashes.

        Your situation doesn't meet the criteria for a good reason to use symbolic references in any code that you intend to keep around on your hard drive for more than a few minutes. You've stated pretty clearly, however, that you're not open to other alternatives; you know what you want, and just needed us to show you how to do it. But as the old saying goes, "now you have two problems."

        It's easy to forget that the symbol table is little more than just a special hash. All of Perl's package globals live in hashes. Perl is implemented to hide that from you most of the time, but behind the scenes, they're there. You don't usually need to fiddle with Perl's hashes when you can create your own to fiddle with instead.


        Dave

        %Bet01 = ( 230 => 2, 238 => 5, 224 => 1,); %Bet05 = ( 101 => 2, 103 => 2, 0 => 4, );
        Why not simply have "Bet01", "Bet05", ... as keys in a hash? For example:
        my %bets = ( 'Bet01' => { 230 => 2, 238 => 5, 224 => 1 }, 'Bet05' => { 101 => 2, 103 => 2, 0 => 4 }, );

Re: variable as hash name
by eyepopslikeamosquito (Archbishop) on Dec 08, 2012 at 07:54 UTC

    PLEASE, don't tell me I dont want to do this. I want to do exactly what I said above. Not to use hash of hashes or anything else. I DO want to use a variable as a hash name and I'm aware of the risks.
    To help you understand why you've copped a few downvotes, it would have been more polite to explain up front why you want to use symbolic references, rather than to issue a command to the unpaid volunteers who answer questions here. Understanding the why behind your question makes it more interesting for us and makes it more likely that others who read this node in the future will learn from it (this topic is discussed in more detail at the XY Problem). Please remember that this site is a community of Perl programmers, a community that encourages everyone to grow and learn from each other.

    Anyway, welcome to the monastery. If you become involved in Perl monks, not just asking for help, but answering the questions of others, you should have a lot of fun and your Perl will improve out of sight.

      You see, is not that I'm not open to alternatives. Is just that I read a lot of foruns adressing this problem (of use variavles as variables name) and as much as I tried, I was unable to use hash of hashes to solve my needs.

      So I wanted to use the dirt way because I needed to solve it fast. But YES, I would like to improve my code to not have to disable strict. And I am thankfull for your help.

      So here is the code I'm using now. Any improvement is welcome.

      #!/usr/bin/perl #use strict; use warnings; my $input_data = << 'REPORT_OF_INPUT'; Bet01;;Bet05;;Bet06;;Bet12;; 230;238;101;103;138;146;112;116;; 230;238;101;103;146;146;108;112;; 224;238;0;0;146;146;110;118;; 238;238;0;0;146;146;112;114;; REPORT_OF_INPUT my @inputs = split "\n", $input_data; my $line = shift @inputs; $line =~ s/;;/;/g; my @loci_codes = split ";", $line; foreach $line ( @inputs ) { my @alelos = split ";", $line; foreach my $locus ( @loci_codes ) { no strict 'refs'; my $allele1 = shift @alelos; my $allele2 = shift @alelos; ${$locus}{$allele1} += 1; ${$locus}{$allele2} += 1; } } foreach $locus ( @loci_codes ) { foreach my $key ( keys ( %{$locus} ) ) { print "$locus ", $key, " = ", $$locus{$key}, "\n"; } }

      I'm using the data inside the code here just to make it easier for decode. But in my real program the content of $input_data will come from an external file. So its content may vary.

        Here's your program modified to work with use strict:

        #!/usr/bin/perl use strict; use warnings; my $input_data = << 'REPORT_OF_INPUT'; Bet01;;Bet05;;Bet06;;Bet12;; 230;238;101;103;138;146;112;116;; 230;238;101;103;146;146;108;112;; 224;238;0;0;146;146;110;118;; 238;238;0;0;146;146;112;114;; REPORT_OF_INPUT my @inputs = split "\n", $input_data; my $line = shift @inputs; $line =~ s/;;/;/g; my @loci_codes = split ";", $line; my %LOCI; foreach my $line ( @inputs ) { my @alelos = split ";", $line; foreach my $locus ( @loci_codes ) { my $allele1 = shift @alelos; my $allele2 = shift @alelos; $LOCI{$locus}{$allele1} += 1; $LOCI{$locus}{$allele2} += 1; } } foreach my $locus ( @loci_codes ) { foreach my $key ( keys ( %{$LOCI{$locus}} ) ) { print "$locus ", $key, " = ", $LOCI{$locus}{$key}, "\n"; } }

        Is that so hard?

        perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: variable as hash name
by Anonymous Monk on Dec 08, 2012 at 09:19 UTC

    PLEASE, dont't tell me I dont want to do this. I want to do exactly what I said above. Not to use hash of hashes or anything else. I DO want to use a variable as a hash name and I'm aware of the risks.

    So you know that article, and countless nodes, that explains why doing this is dangerous? It also explains exactly how to do it!

    So go ahead and RTFM the FAQ, that is why it exists, so you can read it

    using a string as a SCALAR ref while "strict refs" in use

Re: variable as hash name
by Athanasius (Archbishop) on Dec 08, 2012 at 04:43 UTC

    It can be done using eval even with strict:

    14:36 >perl -Mstrict -MData::Dump -wE "my $variable = 'name01'; eval q +q[our %{$variable} = ( valorC => 'value03', valorD => 'value04', );]; + eval qq[our %{$variable}; dd %{$variable},;];" ("valorD", "value04", "valorC", "value03") 14:36 >

    Why you’d want to do it is, of course, another question altogether...

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      To count the number of times each element is present. But keep in mind each two consecutive elements of a line will be in a 'group' different than the others.

      And I can not know at first how many groups there will be.

Re: variable as hash name
by BillKSmith (Monsignor) on Dec 08, 2012 at 19:54 UTC
    Early in my tenure at the monastery, I made the mistake of critizing BrowserUK for a similar use of symbolic reference. His rather insulting reply Re^3: Passing arguments to functions. does make a good case for their use.
    Bill

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (5)
As of 2024-03-19 11:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found