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

Hello All,

I have written a sub routine to factorize a set of equations symbolically. What appears to be happening is that in the subroutine, I am factoring an equation passed as an argument and storing the variable and coefficients associated with that variable in a hash; whose key is the variable and value is the array of coefficients. Within the routine I am using Dumper to check how the hash is getting populated. On factorizing with respect to the first variable, I see the coefficients get recorded correctly in the hash. When the next variable is factored and recorded in the has, I see the coefficients of the next variable get recorded correctly but the firs variable coefficients appears to have been clobbered.. Finally the subroutine returns an array of hashes has recorded the coefficients in an array ( value ) with respect to the variables (keys) for each equation ( equation set ). I am also having difficulty accessing the contents of the returned array of hashes from the subroutine... Any helps would be very appreciated; code is attached below -

#!/usr/bin/perl use Data::Dumper; #use strict; my $DEBUG1=1; my @eq1; my @eq2; my @eqn_set; my @variables; my @factored_eqns; ################################################################ #2 equations( variables are x1 and x2 ) #Representations #eq1 = 2*a*b*x1 + 4*a*c*x2 + 2*a*d*x2 + 4*a*e*x2 #eq2 = 4*m*n*x1 + 2*m*o*x2 + 4*m*p*x1 + 2*m*q*x2 ################################################################ @eq1 = ("2*a*b*x1", "4*a*c*x2", "2*a*d*x1", "4*a*e*x2"); @eq2 = ("4*m*n*x1", "2*m*o*x2", "4*m*p*x1", "2*m*q*x2"); ################################################################# #Form the equation set ################################################################# @eqn_set = (\@eq1, \@eq2); ################################################################## #Return an array of hashes; hash 1 corresponds to a factored #form of eqn1; hash 2 corresponds to a factored form of eqn2 #Returns- #@factored_eqns = (%factored_eqn1, %factored_eqn2); #Where - #$factored_eqn1{"x1"} = ("2*a*b", "2*a*d"); #$factored_eqn1{"x2"} = {"4*a*c", "4*a*e"}; #$factored_eqn2{"x1"} = ("4*m*n", "4*m*p"); #$factored_eqn3{"x2"} = {"2*m*o", "2*m*q"}; ################################################################## @variables = ("x1", "x2"); @factored_eqns = &factorize(\@eqn_set,\@variables); ################################################################### #Now Dump the result of the factorization ################################################################### my $item; foreach $item (@factored_eqns){ print Dumper \%{$item}; } #################################################################### #The factorize subroutine --- #################################################################### sub factorize{ my @this_eqn_set = @{$_[0]}; my @this_variable_set = @{$_[1]}; my $equation; my $variable; my $term; my @factors = (); my %coefficient_array; my $ii=0; my @factored_rows=(); #clear the coefficient array hash undef %coefficient_array; print "Factorize.....\n"; #for each equation in the equation set foreach $equation (@this_eqn_set){ #then for each variable in the eqution foreach $variable (@this_variable_set){ #then for each term in the equation foreach $term (@{$equation}){ #if the term contains the variable currently be factored if($term =~ /\*$variable/){ #extract the variable coefficient from the term $term =~ s/\*$variable//; #save the coefficient for that variable in that term for t +hat equation push (@factors, $term); } }#End processing the term #save the coefficients for that variable in that row $coefficient_array{$variable} = \@factors; #Debug to check that the coefficient array is getting populate +d if($DEBUG1==1){ print "DEBUG1: Coefficient Array Pass $ii -\n"; print Dumper \%coefficient_array; print "D1*****D1******\n"; $ii++; } #clear the factors array for the next variable @factors=(); #the coefficient array at this satge should be a hash whose #keys are the variables and values are the coefficient terms f +or #that variable; we save the hash into the equation set retrun +array push(@factored_rows,%coefficient_array); }#End processing variable in the equation #reset the coefficient array for the the next equation undef %coefficient_array; }#End processing the equation of the equation set #return the factored rows to the calling routine return(@factored_rows); }

Replies are listed 'Best First'.
Re: Help !!! Symbolic Math Factroize Subroutine is clobbering my hash
by Anonymous Monk on Dec 25, 2014 at 19:27 UTC
    push(@factored_rows,%coefficient_array);
    should be
    push(@factored_rows, \%coefficient_array);
    otherwise a hash gets flattened in a list of keys and values. Without '\' it's as if you wrote if like that:
    push(@factored_rows, 'hash_key1', $value1, 'hash_key2', $value2);

    (hash is not an array, btw, they are totally different things)

    Also, your program is hard to read. Some advice:
    @factored_eqns = &factorize(\@eqn_set,\@variables);
    '&' in factorize is completely unnecessary.
    my @factored_eqns; ... @factored_eqns = factorize(\@eqn_set,\@variables);
    Don't declare a bunch of variables all at the top of the program/sub. They're not much better then global variables when used that way (and it's very write-only). Perl is not C89. Instead of:
    my @factored_eqns; ... @factored_eqns = factorize(\@eqn_set,\@variables); ... my $term; ... foreach $term(@{$equation}) { ...
    do
    my @factored_eqns = factorize(\@eqn_set,\@variables); ... foreach my $term (@{ $equation }) { ...
    Indent your code properly. Rather then
    foreach $equation (@this_eqn_set){ #then for each variable in the eqution foreach $variable (@this_variable_set){ ...
    Write it like so:
    for my $equation (@this_eqn_set) { for my $variable (@this_variable_set) { ...
    (for and foreach are the same thing)

    Uncomment strict. And add use warnings;

      Hi,

      Thanks for the advice... I restructured the code base on your observations... A question would be what happens to an array that is being used as a temporary container within a subroutine...

      sub foo{ my @this_array; my %this_hash; foreach....{ push(@this_array, $something); $this_hash{$key} = \@this_array; #reset this_array for some other operation @this_array=(); $key = $next_key; $something = $some_other_thing; } return(\%this_hash); }
      I was wondering if the hash that is returned by this subroutine will map all keys but the last one to NULL objects. This is because each time the array reference is stored in the hash, it is null-ed thereafter, and therefore the stored array references are now pointing to a NULL objects.. What would be the proper way to use an array as a temporary container ??
        #reset this_array for some other operation @this_array=();

        Why would you reset @this_array; Why not just use @that_array for the other operation?


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
        push @{ $this_hash{$key} }, $something;
        Perl will automagically create a new anonymous array and associate it with $this_hash{$key}, even if the hash didn't even have this key before (it's called 'autovivification'). OTOH, if the key is already pointing to an array, Perl will reuse it.