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

I wanna write a general function/sub that will arbitrarily compose/update a hash of arrays, say, %distri. My current design is to pass an element of %distri, which should be an array reference, to the sub, and assign an array to it (the reference) in the sub.

use strict; # Hash of arrays. my %distri; my @probExpr=qw(c_K n_cK); foreach (@probExpr) { print "$_\n"; #%distri{$_}=(); %distri{$_}=[]; } &hashOfArraysRef(\$distri{$probExpr[0]}); print "\$distri{$probExpr[0]}[5]\t"; print "$distri{$probExpr[0]}[5]\n"; #&aOa; 1; sub hashOfArraysRef #(\@) { my $arrayRef=$_[0]; # Dereference the passed-in reference to an array. @$arrayRef=(0..9); #@$arrayRef=[0..9]; }
But it can't compile:
syntax error at ./dataStructure.pl line 12, near "%distri{"
Can't use global @_ in "my" at ./dataStructure.pl line 23, near "=$_"

Replies are listed 'Best First'.
Re: Function composing a hash of arrays.
by liverpole (Monsignor) on Dec 26, 2006 at 13:56 UTC
    Hi dislimit,

    You're very close.  jettero has already mentioned that you need to use the appropriate sigil; '$' instead of '%' for your hash.

    You also are expecting a reference to a list in your subroutine hashOfArraysRef, but you have an extra level of indirection here:

    &hashOfArraysRef(\$distri{$probExpr[0]});

    because $distri{$probExpr[0]} is already holding an array reference.  If you change it to this:

    &hashOfArraysRef($distri{$probExpr[0]});

    you should find that it works closer to the way you need.


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: Function composing a hash of arrays.
by jonadab (Parson) on Dec 26, 2006 at 14:22 UTC
    foreach (@probExpr) { print "$_\n"; #%distri{$_}=(); %distri{$_}=[]; }

    Others have explained how to fix the syntax here, but even with that change, all this does is initialize each value of %distri to (a reference to) an empty anonymous array. Initializing variables to "empty" values is common practice in languages like C, but in Perl it is usually unnecessary and sometimes a sign of poor design. I will come back to this particular case in a moment...

    &hashOfArraysRef(\$distri{$probExpr[0]});

    This feels strange to me, and somewhat obfuscated, for code whose basic purpose is to assign something to that hash element. Yes, it is clear how it works when I look at the code for the subroutine, but apart from that additional context _this_ line could be... misleading. It would seem much more straightforward like this:

    $disti{$probExpr[0]} = function_that_returns_anonymous_array();

    Or, assuming your function really wants to assign different anonymous arrays to different elements, perhaps something more like this:

    $disti{$probExpr[0]} = function_that_returns_anonymous_array($probEx +pr[0]);

    Or even...

    $disti{$_} = function_that_returns_anonymous_array($_) for @probExp +r;

    Then the basic flow of what's going on would be fairly clear _without_ seeing the subroutine definition. Since one of the major purposes of subroutines is to factor out details so that the main flow of the code can be seen, that would seem a desirable outcome.

    Further, the initialization loop above would then become superfluous, further simplifying the code.


    Sanity? Oh, yeah, I've got all kinds of sanity. In fact, I've developed whole new kinds of sanity. You can just call me "Mister Sanity". Why, I've got so much sanity it's driving me crazy.
Re: Function composing a hash of arrays.
by wfsp (Abbot) on Dec 26, 2006 at 14:31 UTC
    Extending your idea a little and hopefully clarifying the points made by the other monks.

    #!/bin/perl5 use strict; use warnings; use Data::Dumper; my $distri; $distri = { # hash ref one => [qw{1 2 3}], # array ref two => [qw{3 4 5}], three => [qw{6 7 8}], }; my ($aref); my $key_new = 'four'; $aref = [qw{9 10 11}]; $distri = update_hash($distri, $key_new, $aref); my $key_existing = 'one'; $aref = [qw{12 13 14}]; $distri = update_hash($distri, $key_existing, $aref); print Dumper $distri; sub update_hash { # unpack and copy the arguments my ($href, $key, $aref) = @_; # key will be created if it doesn't exist # (autovivified) push @{$href->{$key}}, @{$aref}; # append # $href->{$key} = $aref; # overwrite # return the copy # (leaving the original intact) return $href; }
    output:
    ---------- Capture Output ---------- > "C:\Perl\bin\perl.exe" _new.pl $VAR1 = { 'three' => [ '6', '7', '8' ], 'one' => [ '1', '2', '3', '12', '13', '14' ], 'two' => [ '3', '4', '5' ], 'four' => [ '9', '10', '11' ] }; > Terminated with exit code 0.
Re: Function composing a hash of arrays.
by jettero (Monsignor) on Dec 26, 2006 at 13:52 UTC

    In perl, that leading type indicator indicates the context. You want $distri{$_} = []; since you're assigning a scalar

    -Paul