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

PerlMonks =),

I'm playing with an 'array of hashes of arrays' and having some problems with data manipulation and was wondering if there might be some incite from above to help out...


We have the following data structure,

List (array of things)
|->hash of data about things (name, inputs, outputs)
|->some data elements require arrays of data. (can have multiple inputs and outputs)

In the following code the AddConnection subroutine works fine and pushes the $from and $to variables into the arrays of the hash of object properties, which belongs to an array of objects.
sub AddConnection { my ($from, $to) = @_; for $l ( @list ) { if ( $l->{"name"} eq $from ) { push @{$l->{"outputs"}}, $to; } if ( $l->{"name"} eq $to ) { push @{$l->{"inputs"}}, $from; } } } sub AddRandomConnection { my @arr; my $i = scalar(@inputlist); print "we have $i elements to choose from\n"; my $rand_i = int(rand($i)); my $from = $inputlist[$rand_i]; print "we picked $rand_i : $from\n"; my $o = scalar(@inputlist); print "we have $i elements to choose from\n"; my $rand_o = int(rand($o)); my $to = $outputlist[$rand_o]; print "we picked $rand_o : $to\n"; #TODO: need to make sure you can't come to and from same node for $l ( @list ) { if ( $l->{"name"} eq $from ) { push @{$l->{"outputs"}}, $to; } if ( $l->{"name"} eq $to ) { push @{$l->{"inputs"}}, $from; } } }

The issue comes with the second function AddRandomConnection which employs the same code to access the data, but returns the following error upon execution.

we have 2 elements to choose from
we picked 0 : O1
we have 2 elements to choose from
we picked 1 : I2
Modification of a read-only value attempted at <myfile> line 73.


As can be seen the error message eludes to accessing bad data or some such, but the selection of elements 0 and 1 are within the limits of the array size of two for each element.
Can anyone see my oversight?

Regards Paul =)

Edit by castaway - Added readmore tags

Replies are listed 'Best First'.
Re: Array of hashes of Arrays
by dragonchild (Archbishop) on Feb 21, 2005 at 16:14 UTC
    This isn't going to answer your question, but why aren't you using Graph? That would seem to solve your real problem ...

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

      Dragonchild,

      I'm not using Graph because I haven't come across it before to be honest. Graph seems to provide dumping and manipulation routines, but ultimately I need this to be fast so routing through an external nodule isn't ideal (not that my code is the pinnacle of fast execution).

      I'm not sure what you mean by 'solve your real problem', but if you mean just ensuring that my data structure is sound to begin with I usually just data dump, and perhaps should have provided this in my initial question for further clarity.

      $VAR1 = { 'name' => 'I1', 'inputs' => '-' }; $VAR2 = { 'name' => 'I2', 'inputs' => '-' }; $VAR3 = { 'outputs' => '-', 'name' => 'O1' }; $VAR4 = { 'outputs' => '-', 'name' => 'O2' }; After we do an AddConnection("I1","O2") $VAR1 = { 'outputs' => [ 'O2' ], 'name' => 'I1', 'inputs' => '-' }; $VAR2 = { 'name' => 'I2', 'inputs' => '-' }; $VAR3 = { 'outputs' => '-', 'name' => 'O1' }; $VAR4 = { 'outputs' => '-', 'name' => 'O2', 'inputs' => [ 'I1' ] }; we have 2 elements to choose from we picked 0 : O1 we have 2 elements to choose from we picked 0 : I1

      This shows that the data structure is sound and that the Addconnection subroutine does it's thing...
      Any further Ideas?

      Regards Paul.
        but ultimately I need this to be fast so routing through an external nodule isn't ideal (not that my code is the pinnacle of fast execution).

        Get it working, then get it fast. You have no idea how fast Graph is or isn't until you have tried it and benchmarked it. In fact, it may be so fast that you have no idea how you lived without it.

        Further more, "routing through an external module" is no slower than using a module you wrote yourself. And, it may be faster for several reasons:

        • The author probably knows the problemspace better than you do, so they have the right algorithm(s).
        • The author probably has some caching mechanisms in place.
        • The author may have even rewritten critical parts in XS, providing up to a 100x speed improvement

        Try Graph. If it's not fast enough, I have a faster version that assumes one edge between each connected vertex. At least study the Graph code - at least it works.

        Being right, does not endow the right to be rude; politeness costs nothing.
        Being unknowing, is not the same as being stupid.
        Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
        Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: Array of hashes of Arrays
by phaylon (Curate) on Feb 21, 2005 at 16:50 UTC
    That's what matters :D

    Ordinary morality is for ordinary people. -- Aleister Crowley
      phaylon, Good spot on the typo, its wasn't ultimately the main cause, but I think its happy now =).
      Program....
      use strict; use Data::Dumper; my @inputlist; my @outputlist; my @list; sub AddInput { my $name = $_[0]; # push @list, { name => $name, inputs => "-" }; push @list, { name => $name }; push @outputlist, $name; } sub AddOutput { my $name = $_[0]; # push @list, { name => $name, outputs => "-" }; push @list, { name => $name }; push @inputlist, $name; } sub AddNeuron { my $name = $_[0]; push @list, { name => $name }; push @inputlist, $name; push @outputlist, $name; } sub AddConnection { my ($from, $to) = @_; for my $l ( @list ) { if ( $l->{"name"} eq $from ) { push @{$l->{"outputs"}}, $to; } if ( $l->{"name"} eq $to ) { push @{$l->{"inputs"}}, $from; } } } sub AddRandomConnection { my @arr; my $i = scalar(@inputlist); print "we have $i elements to choose from\n"; my $rand_i = int(rand($i)); my $from = $inputlist[$rand_i]; print "we picked $rand_i : $from\n"; my $o = scalar(@outputlist); print "we have $i elements to choose from\n"; my $rand_o = int(rand($o)); my $to = $outputlist[$rand_o]; print "we picked $rand_o : $to\n"; #TODO: need to make sure you can't come to and from same node for my $l ( @list ) { if ( $l->{"name"} eq $from ) { push @{$l->{"outputs"}}, $to; } if ( $l->{"name"} eq $to ) { push @{$l->{"inputs"}}, $from; } } } AddInput("I1"); AddInput("I2"); #AddInput("I3"); AddOutput("O1"); AddOutput("O2"); #AddNeuron("N1"); #AddNeuron("N2"); print Data::Dumper->Dump([@list]); print "Add a direct connection\n"; AddConnection("I1", "O2"); print Data::Dumper->Dump([@list]); print "Add a random connection\n"; AddRandomConnection(); print Data::Dumper->Dump([@list]);

      Gives output...
      $VAR1 = { 'name' => 'I1' }; $VAR2 = { 'name' => 'I2' }; $VAR3 = { 'name' => 'O1' }; $VAR4 = { 'name' => 'O2' }; Add a direct connection $VAR1 = { 'outputs' => [ 'O2' ], 'name' => 'I1' }; $VAR2 = { 'name' => 'I2' }; $VAR3 = { 'name' => 'O1' }; $VAR4 = { 'name' => 'O2', 'inputs' => [ 'I1' ] }; Add a random connection we have 2 elements to choose from we picked 1 : O2 we have 2 elements to choose from we picked 1 : I2 $VAR1 = { 'outputs' => [ 'O2' ], 'name' => 'I1' }; $VAR2 = { 'name' => 'I2', 'inputs' => [ 'O2' ] }; $VAR3 = { 'name' => 'O1' }; $VAR4 = { 'outputs' => [ 'I2' ], 'name' => 'O2', 'inputs' => [ 'I1' ] };


      Regards Paul