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

Hi PerlMonks,

When i add a hash ref to an array , an additional element is added to the code. Looking for the reason for this and how i can avoid it.

The code is mentioned below , with --HERE indicating the code that adds the hash array element and the corresponding additional elements added.

#!/usr/bin/perl use Data::Dumper; my $cmd = "dfm host add [ -N ] [ -U <hostLogin> -P <hostPassword> ] { +all | <objects> ... | -H <hosting-storage-system> <vfiler> ... | - +T <trip> | -S <saf> } <end>"; print "\nCMD : $cmd\n"; my $arg_hash = getCmdParams($cmd); print Dumper ($arg_hash); sub getCmdParams { my $cmd = shift; my @temp = split(/\s+/,$cmd); my (@param,@switch,@arg,@alt_par); my ($line,$alt_cnt); my @mandate = (); my @optional = (); my $alt_line = ""; my %switch_param; my $switch_optional; foreach $line(@temp) { # handle alternate options if ($line =~ /^\{/){ $alt_cnt = 1;next; } $alt_cnt = 0 if ($line =~ /\}$/); #print "\nLINE : $line\nALT_CNT : $alt_cnt \n"; if ($alt_cnt == 1){ $alt_line.= $line; next ; } if ($alt_line ne ""){ print "\nLINE : $line ALT LINE: $alt_line\n"; @alt_par = split(/\|/,$alt_line); print "\nALT PAR LIST\n"; foreach (@alt_par){ print "\nOPTION : $_\n"; if (/.*?(\-[a-zA-Z]){1}.*?\<(.*?)\>/i){ $switch_optional->{"$1"} = $2; @optional = (@optional,$switch_optiona +l); ----HERE $switch_param{"optional"} = [@optional +]; }elsif(/.*?([a-zA-Z]+).*/i){ @optional = (@optional,($1)); $switch_param{"optional"} = @optional; }else{ } $alt_line =""; next; } } # handles assigning switches to their respective parameters , handles +single switches also, handles mandatory parameteres without switches if ($line =~ /^\-[a-zA-Z]{1}/){ #print "\n$line\n";getc(); push (@switch,$line); } if (($line =~ /\<(.*?)\>/g)){ push (@arg,$1); $switch_param{shift @switch} = "" if ((scalar @switch) + > 1); $switch_param{shift @switch} = pop @arg unless ((scala +r @switch) == 0); } $switch_param{shift @switch} = "" if ((scalar @switch) > 1); $switch_param{"mandatory"} = [(@mandate,@arg)] if ((scalar @ar +g) > 0); } return \%switch_param; }

The output :

$VAR1 = { '-P' => 'hostPassword', '-N' => '', 'mandatory' => [ 'end' ], 'optional' => [ 'all', 'objects', { '-T' => 'trip', '-H' => 'hosting-storage-system', '-S' => 'saf' }, $VAR1->{'optional'}[2], ----HERE $VAR1->{'optional'}[2] ----HERE ], '-U' => 'hostLogin' };

Thanks in advance!

Replies are listed 'Best First'.
Re: Hash ref assignment to array results in anomaly
by Corion (Patriarch) on Jul 24, 2009 at 11:54 UTC

    Then don't do that.

    Maybe you could explain what you want to happen instead? If you want to create a new copy of $switch_optional every time you come around the loop, maybe reinitialize $switch_optional at the top of the loop instead of having it in scope for the whole function. If you just want to add it once, just add it once, instead of every time around the loop. Maybe you don't even want to use a hash, but simply add the switches to your list. It's hard to tell without you telling us.

    Also consider whether it's really necessary to post a wall of code and add "HERE" markers. You could have spent some time reducing that code so that it still exhibits the problem, but is so small that you don't need any more HERE markers.

      When the hash ref is added as an element to the array whose key is "optional" , the elements $VAR1->{'optional'}2, $VAR1->{'optional'}2 also get added to the array. I do not understand why.

      I apologise for posting the whole code. I thought that probably some other part of the code was effecting such behavior.

        The problem stems, as I already described, from your top-level declaration of $switch_optional. You're not initializing it there, so Perl initializes it to a hash reference when you first use it as a hash reference. As you never change the value of the reference, Perl keeps that value. You then push, in the loop, that reference onto your array. So Perl pushes that same value of the same reference to your array. Three times in total.

        If you reduce your program to the relevant part, you actually find out what parts are irrelevant and what parts are relevant. This is an important capability to have or to evolve, if you want to have any chance at succeeding to program. So please, show the effort and reduce your program to only the relevant parts.

Re: Hash ref assignment to array results in anomaly
by GrandFather (Saint) on Jul 24, 2009 at 12:06 UTC

    What do you expect to see?

    Have you debugged through the code to see if the contents of the variables are as you expect at each stage of execution?


    True laziness is hard work
      What do i expect to see - When the hash ref is added as an element to the array whose key is "optional" , the elements $VAR1->{'optional'}2, $VAR1->{'optional'}2 also get added to the array. I do not understand why.
      i have tried to debug the code with a whole lot of print statements , but still not managed to find out why the extra elements are added.

        As Corion has suggested, a very effective tool for finding where a problem lies is to reduce the code to just enough to see the issue. You may find some of the techniques described in I know what I mean. Why don't you? helpful for focussing on just the code you need to demonstrate the problem. In the case of your problem you might reduce the code to:

        use strict; use warnings; use Data::Dumper qw(); use Data::Dump::Streamer qw(); my %switch_param; my $switch_optional; $switch_optional->{foo} = 'bar'; my @optional; for (1 .. 3) { @optional = (@optional, $switch_optional); } $switch_param{"optional"} = [@optional]; Data::Dump::Streamer::Dump \%switch_param; print "\n\n"; print Data::Dumper::Dumper (\%switch_param);

        which prints:

        $HASH1 = { optional => [ { foo => 'bar' }, 'V: $HASH1->{optional}[0]', 'V: $HASH1->{optional}[0]' ] }; $HASH1->{optional}[1] = $HASH1->{optional}[0]; $HASH1->{optional}[2] = $HASH1->{optional}[0]; $VAR1 = { 'optional' => [ { 'foo' => 'bar' }, $VAR1->{'optional'}[0], $VAR1->{'optional'}[0] ] };

        That might, or might not, make it clear to you where the issue is, but it does make it much clearer to us not only where, but what the problem is. Note that the code uses two different data structure dumping routines which show the output somewhat differently. You may find the Streamer version more clearly illustrates the issue.

        Update added Streamer dump.


        True laziness is hard work
Re: Hash ref assignment to array results in anomaly
by ig (Vicar) on Jul 24, 2009 at 13:01 UTC

    I have added some additional print statements to your program. These will show you, step by step, how the offending elements are added to your data structure.

    If you run this modified version of your program, you may be able to see which commands are adding the "additional" elements. You should be able to figure out which statement is adding the elements if you follow it one step at a time.

    An alternative to adding print statements is to run your program in the debugger. Perl has a nice, built-in debugger. Some people prefer using the debugger and some people prefer using print statements. Either way, you can see how your statements are changing your data.

    Solving the problem is a little tricky, given your data structure. What you need to do is append $switch_optional to the @optional array only once, instead of once each time you find a flag with an argument.

    There is an error when 'all' and 'objects' are added to $switch_param{"optional"}. Can you see it? It is corrected when the hash reference is added. (hint: hash values are scalars. They can be references to arrays, but not whole arrays. What do you get when you evaluate an array in scalar context?).

      thank you for pointing out the error with respect to the array in scalar context.
Re: Hash ref assignment to array results in anomaly
by jwkrahn (Abbot) on Jul 24, 2009 at 15:03 UTC

                                    @optional = (@optional,$switch_optional);

    and

                                    @optional = (@optional,($1));

    are better written as:

                                    push @optional, $switch_optional;

    and

                                    push @optional, $1;