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

Over the years there have been numerous times when I passed arrays by reference to subroutines. I have often been a little challenged by it. Now I want to start another program and I'd like to seek a little wisdom before I get started.

I want to be able to easily pass data to various subroutines by using references. To build an example for this posting I'll use an array "A" to hold the references to the data. Thus, my calling code would look like this:
&fetch_data(\@A);
&process_data(\@A);
&save_results(\@A);
To build this example let's assume I want to process the data associated with several files, experiments, data sets, widgets or whatever. I want to have an array for each data set (B0, B1, ... Bn) that contains the references needed to access that specific data set. The upper level array (A) would contain references to these arrays of references. Thus:
$A[0] = \@B0; # array B0 contains references for data set index_0
$A[1] = \@B1; # array B1 contains references for data set index_1
etc. 
Now assume the simplified case where each data set has one text array and one hash. References to the data array @Ctn and data hash @Chn will be stored in the data set specific array of references @Bn. Thus:
$B0[0] = \@Ct0; # array Ct0 contains text data for data set index_0
$B0[1] = \%Ch0; # hash  Ch0 contains hash data for data set index_0
$B1[0] = \@Ct1; # array Ct1 contains text data for data set index_1
etc.
I have made everything described above work properly in the included sample code. I have two main questions:

1) Is my syntax and method of working with the data valid? I would hate to be using references to "spray data" all over the computer and sooner or later corrupt something or crash the system.

2) The $_[0]->[0][0][0] syntax for recovering data is not too bad. But the ${${${$_[0]}[1]}[0]}[2] = syntax for storing data is rather messy. I tried to store using the same syntax as recovering but it did not work. Is there cleaner syntax for storing data?

My sample code is in the READMORE area below.
#!/user/bin/perl use warnings; use strict; my (@A, $A); # single, top level array of references my (@B0, $B0); # mid level array of references for data set index_0 my (@B1, $B1); # mid level array of references for data set index_1 my (@Ct0, $Ct0); # low level array storing text for data set index_0 my (@Ct1, $Ct1); # low level array storing text for data set index_1 my %Ch0; # low level hash storing key-value pairs for data set index_0 my %Ch1; # low level hash storing key-value pairs for data set index_1 print "\nSAMPLE PROGRAM FOR PERLMONKS QUESTION\n"; $A[0] = \@B0; # array B0 contains references for data set index_0 $A[1] = \@B1; # array B1 contains references for data set index_1 $B0[0] = \@Ct0; # array Ct0 contains text data for data set index_0 $B1[0] = \@Ct1; # array Ct1 contains text data for data set index_1 $B0[1] = \%Ch0; # hash Ch0 has key-value pairs for data set index_0 $B1[1] = \%Ch1; # hash Ch1 has key-value pairs for data set index_1 &fetch_data(\@A); &process_data(\@A); &store_data(\@A); sub fetch_data { # populating storage for data set index_0 ${${${$_[0]}[0]}[0]}[0] = 10; ${${${$_[0]}[0]}[0]}[1] = 11; ${${${$_[0]}[0]}[0]}[2] = 12; ${${${$_[0]}[0]}[1]}{"dog"} = 15; ${${${$_[0]}[0]}[1]}{"cat"} = 16; ${${${$_[0]}[0]}[1]}{"bird"} = 17; # populating storage for data set index_1 ${${${$_[0]}[1]}[0]}[0] = 20; ${${${$_[0]}[1]}[0]}[1] = 21; ${${${$_[0]}[1]}[0]}[2] = 22; ${${${$_[0]}[1]}[1]}{"dog"} = 25; ${${${$_[0]}[1]}[1]}{"cat"} = 26; ${${${$_[0]}[1]}[1]}{"bird"} = 27; } sub process_data { # change two items in data set index_1 just to prove I can ${${${$_[0]}[1]}[0]}[2] = 24; ${${${$_[0]}[1]}[1]}{"bird"} = 29; } sub store_data { # printing to screen is good "storage" for a sample program print "TEXT: $_[0]->[0][0][0]\n"; print "TEXT: $_[0]->[0][0][1]\n"; print "TEXT: $_[0]->[0][0][2]\n"; print "\n"; print "HASH: $_[0]->[0][1]{\"dog\"}\n"; print "HASH: $_[0]->[0][1]{\"cat\"}\n"; print "HASH: $_[0]->[0][1]{\"bird\"}\n"; print "\n"; print "TEXT: $_[0]->[1][0][0]\n"; print "TEXT: $_[0]->[1][0][1]\n"; print "TEXT: $_[0]->[1][0][2]\n"; print "\n"; print "HASH: $_[0]->[1][1]{\"dog\"}\n"; print "HASH: $_[0]->[1][1]{\"cat\"}\n"; print "HASH: $_[0]->[1][1]{\"bird\"}\n"; }

Replies are listed 'Best First'.
Re: multiple layers of referencing and dereferencing
by wfsp (Abbot) on Jun 23, 2009 at 02:45 UTC
    ${${${$_[0]}[0]}[1]}{"dog"} = 15;
    ewwww! You'll be giving Perl a bad name. :-)
    I tried to store using the same syntax as recovering but it did not work.
    Are you sure? this works ok here:
    #! /usr/bin/perl use strict; use warnings; use Data::Dumper; $Data::Dumper::Indent = 2; my $data; $data->[0][0][0]{dog} = 15; print Dumper $data;
    $VAR1 = [ [ [ { 'dog' => 15 } ] ] ];
Re: multiple layers of referencing and dereferencing
by Herkum (Parson) on Jun 23, 2009 at 03:12 UTC

    There are two measures when it comes to coding.

    1. Does it do what it is supposed to do?
    2. Can I look at the code and easily understand what the code is supposed to do?

    Most programmers(which are bad programmers) stop at step 1 because most people are not going to check the code that the wrote unless there was a bug.

    As for 2, can you really tell me that code is understandable? Come on, it is just a bunch of numbers, it conveys nothing except you learned what a reference is.

    Let me ask you a simple question, do you believe that this a well written program? Do you think our most learned Perl monks like chromatic, corion, or theDamian ( no offense for missing the several hundred other talent monks out there ) would write this sort of code? I believe you know the answer.

      Instead of stating that some of the syntax looked "rather messy" I should have been a little more honest and stated that I don't really like any of it.

      I think that my question and supporting code reflect a well thought out and well organized (please don't flame my yet) attempt by someone that is asking the question because they understand that they still don't quite "see the way". It was never my intention for "not too bad" to suggest "good". I did not like the "not too bad" code, but at least I didn't have to take time to count all the opening and closing curley brackets and square brackets to see if they matched up.

      Perl is easy to get started with and hard to get good at. Perhaps I should have given my question the title "Help me with the issue of most other languages allowing arrays to have multiple dimensions and perl does not".

      Actually, a good tutorial for someone good at perl to write would be "Multi Dimensional Arrays The Perl Way". Many of us "part time programmers" who spend most of our "part time" in other languages find it easy to stray to the "Dark Side" when we try to organize large collections of data in perl. If we are lucky enough to be able to read the full book and fully understand the full book before we program then we might "see the light" before we program. There are a lot of us out here who are not full time programmers and are forced to learn programming in small snapshots.

      Not having multiple dimensions for arrays is one of the areas that cause trouble for "part time" "snapshot" programmers. And, to make matters worse, (and if I recall correctly) when we break code up into separate files to support reusing utility sections then we are forced to use references to pass data to subroutines. Wow, now I understand why I was seeking wisdom.

        The advice I would give you then is that hashes are for context and arrays are for order.

        Things that you don't need to know the order of should be in some sort of hash because the keys of a hash give the data context. Example:

        my @cats = qw(persian siamese cute); my @dogs = qw(doverman bulldog); ## Bad data structure my @data = (\@cats, \@dogs); function( \@data ); ## Good data structure function1({ cats => \@cats, dogs => \@dogs, });

        The first example with a bad data structure requires explicit know of the exact order of your arguments to know where cats and dogs are. In addition if you just deference the data you still don't necessary know what you are working with. Don't do it.

        The second example explicitly designates which array references go with what type of data. Your funtion() will need to deference the data via a name which puts your code into a context which makes it easier to understand.

        sub function1 { my $params = shift; warn "My Dogs\n"; for (@{$params->{'dogs'}}) { warn "\t$_\n" } warn "My Cats\n"; for (@{$params->{'cats'}}) { warn "\t$_\n" } }

        Hope this helps.

Re: multiple layers of referencing and dereferencing
by Marshall (Canon) on Jun 23, 2009 at 04:10 UTC
    The $_[0]->[0][0][0] syntax for recovering data is not too bad.

    I would disagree with that. This looks very obtuse to me! I think that a much better data structure can be constructed. It would be helpful if we backed up and talked about what data you need for each experiment. It sounds like this will wind up being a hash with different values having perhaps reference to list or scalar or simple string or perhaps ref to another hash.

    I would go as far as to say that having an array with heterogeneous data types is a bad idea. One of the "power hitter" features of Perl is that it has very cool iteration functions. Most of the time, it is not even necessary to know the "index" of some individual item at all! This is because instead of say a traditional "for loop" ala for (i=1;i<30;i++){work with a[i]}we have foreach my $thing (@a){do something with thing}. You want each thing in an array to be the same so that these array iterators work well.

    Anyway for each experiment, %experiment, say, perhaps:

    $experiment{'title'}='some text'; $experiment{'data'}= [data set]; $experiment{'test condx'}= some ref to another hash table $experiment{'date run'}='some date';
    If you want to group experiments all together, then it could be that a ArrayOfHash (AoH), is the right way.

    Anyway I think a VAST simplification of your data structure can be made which will result in a VAST improvement in the readability of the code.

    So instead of debating how to make your existing syntax work, I suggest backing up to requirements and then proceeding with a data struct based upon that info.

    Some small points, you don't need &function() to call function anymore, function(...blah) does it!

    Update:
    I wrote some simple code as a demo of how a LoH (list of hash) would work...it illustrates a couple of points: 1) nowhere in the code is any kind of [$i] variable! 2)Rather than "going deeper" to make new data sets, this uses the Regex power of Perl to keep things flat - if the key contains 'data' then the key represents a list of stuff.

      I responded to "not too bad" in an earlier reply.

      Thank you for the suggestions and sample code. I'm printing it and thinking about how I can reorganize my approach to my actual task to take advantage of these "better ways". I like the much cleaner look of it, but I'm going to need a little time to "see the light" for my application.

      Thank you
        Perl has amazing capabilities to generate very complex multi-dimensional data structures. In Perl every dimension until the last one is a reference. Seldom will more than 3 dimensions be needed with the power of the built in data type of hash table especially when combined with Regex. In my example, instead of making a sub-struct with the "data", I just filtered the hash keys to get the "data" keys (data,data1,data3) instead of other keys. This "flattens" the code and reduces one level in the data structure (and very efficiently).

        I looks like to me that you just need a hash table with some heterogeneous values, like pointer to array, scalar, etc. This single structure is then represented in an amalgamation as an array them (pointers, "references" to them).

Re: multiple layers of referencing and dereferencing
by codeacrobat (Chaplain) on Jun 23, 2009 at 06:12 UTC
    Some advices to improve readability:
  • Improve your datastructure.
  • Use the arrow syntax also for assignments.
  • Name and factor out any reference used more than once $B0_1 Is not good enough.
  • Consider using push and pop over indexed array access.
  • Don't quote hash keys dog, cat, bird inside quotes. Use qq{foo"bar"} if necessary

    print+qq(\L@{[ref\&@]}@{['@'x7^'!#2/"!4']});