http://qs321.pair.com?node_id=908753

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

Hi,
I'm having a problem referencing/returning my hash arrays and thought I would ask here ... I've been stuck on this for 2 days already trying to read/understand perldocs etc.! pretty new to Perl so apologies if this is a super-n00b question.
I'm trying to pass an array of hash arrays to-and-from a sub.
E.g. : $F_SCORE$i{$co})where $i is the year counter, and $co is a company name string.

My basic code is this:

At end of sub:

    return (\@date, \@F_SCORE, \@F_SCORE_BOX, \@F_ROA, \@F_CFO, \@F_dROA, \@F_ACCRUAL, \@F_dLEVER, \@F_dLIQUID, \@F_EQOFFER, \@F_dTURN, \@F_dMARGIN);

then in main:

my ($date_ref, $F_SCORE_ref, $F_SCORE_BOX_ref, $F_ROA_ref, $F_CFO_ +ref, $F_dROA_ref, $F_ACCRUAL_ref, $F_dLEVER_ref, $F_dLIQUID_ref, $F_E +QOFFER_ref, $F_dTURN_ref, $F_dMARGIN_ref) = piotroski (\@balancesheet +s, \@cashflows, \@pnl, \@cdata, $co_name, $yahoo_isin, $co_exchange, +$statement_cur, $px_cur); my (@date, @F_SCORE, @F_SCORE_BOX, @F_ROA, @F_CFO, @F_dROA, @F_ACC +RUAL, @F_dLEVER, @F_dLIQUID, @F_EQOFFER, @F_dTURN, @F_dMARGIN) = (@$d +ate_ref, @$F_SCORE_ref, @$F_SCORE_BOX_ref, @$F_ROA_ref, @$F_CFO_ref, +@$F_dROA_ref, @$F_ACCRUAL_ref, @$F_dLEVER_ref, @$F_dLIQUID_ref, @$F_E +QOFFER_ref, @$F_dTURN_ref, @$F_dMARGIN_ref); print "/$date[0]{$co_name}\t$date[1]{$co_name}\t$date[2]{$co_name} +\t$date[3]{$co_name}\t$date[4]{$co_name}\n"; print "/$F_SCORE[0]{$co_name}\t/$F_SCORE[1]{$co_name}\t/$F_SCORE[2 +]{$co_name}\t/$F_SCORE[3]{$co_name}\t/$F_SCORE[4]{$co_name}\n";


Confusingly for me, $date prints each element just fine, whereas none of the others seem to contain any data (that I have find). In the sub itself, they work fine and I can dump F_Score (and other) values to the screen.

Kinda stumped and thought some kind soul here might have an idea! (I'm sure there is a better way to do this too ... I'm all ears!)

Thanks,

Carl

Replies are listed 'Best First'.
Re: Referencing/returning array of hashes from sub
by toolic (Bishop) on Jun 08, 2011 at 18:06 UTC
    my (@date, @F_SCORE, @F_SCORE_BOX,
    This assignment makes @date gobble up all the values on the RHS, leaving the other arrays empty. A smaller example will illustrate this:
    use warnings; use strict; use Data::Dumper; my ($date_ref) = [0..3]; my ($F_SCORE_ref) = [5..7]; my (@date, @F_SCORE) = (@$date_ref, @$F_SCORE_ref); print Dumper(\@date); print Dumper(\@F_SCORE); __END__ $VAR1 = [ 0, 1, 2, 3, 5, 6, 7 ]; $VAR1 = [];

    From perldata:

    You can actually put an array or hash anywhere in the list, but the first one in the list will soak up all the values, and anything after it will become undefined.
    You should consider a different data structure: perldsc
      Thanks. I think I understand; makes sense if all the returns are sequential in memory and @date has no idea how big it 'should' be so gobbles everything up. What data structure would you recommend?

      Thanks,

      Carl

        Dereference your references as you need to work with them. There's no syntactic need to expand them into non-references. Instead of:

        my @expanded = @$reference; for my $element (@expanded) { ... }

        ... you can just as well write:

        for my $element (@$reference) { ... }
Re: Referencing/returning array of hashes from sub
by wind (Priest) on Jun 08, 2011 at 17:57 UTC
Re: Referencing/returning array of hashes from sub
by wfsp (Abbot) on Jun 09, 2011 at 07:20 UTC
    When I'm passing many arguments to a sub I often bundle them up into a hash ref and pass that. Similarly, if the sub returns many arguments I pass them back in a hash ref too. One advantage of using a hash (i.e. named arguments) in such cases is that you're not relying on the position of the arguments. With many arguments that can start to get tricky. Building hashes also let me easily introduce lots of vertical white space. While some monks pour scorn on such a style I find it reads better. So there! :-)

    If your dates and scores are in an AoH you can let Perl load them into a couple of tmp vars and print those rather than a long list of indexed array elements. To my eyes it looks neater and, with well chosen var names, can be self documenting.

    When you're dealing with complex data structures and you're struggling to disentangle them it's worth having a look at Data::Dumper to work out what is happening.

    Untested and doesn't compile.

    my $args = { balance_sheets => \@balancesheets, cashflows => \@cashflows, pnl => \@pn1, # etc. # etc. }; my $F = piotroski($args); my (@dates, @scores); push @dates, $F->{date}[$_]{$co_name} for (0..4); push @scores, $F->{score}[$_]{$co_name} for (0..4); print join(qq{\t}, @dates), qq{\n}; print join(qq{\t}, @scores); sub piotroski { my $args = shift; # # do something with args # my $F = { date => $date_ref, score => $F_SCORE_ref, score_box => $F_SCORE_BOX_ref, roa => $F_ROA_ref, # etc. # etc. } return $F; }
    updated: fixed key name typo