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

Monks, I am still trying to grok array slices, though my understanding is improving thanks to all your help. Now I'm wondering if / how they can /should be used with more complex data structures.

Question 1, can this code be rewritten with an array slice to decrease the repetition in setting my ($color, $fruit)?

Question 2, is this way of dealing with HOA kosher, or am I better off using a HOH here? Would be curious to hear people that love / hate this kind of code, and why.

Thanks for helping me up the learning curve!

use warnings; use strict; my ($idx_color, $idx_fruit) = (0,1); my $hoa = { keyone => ["green","apple"], keytwo => ["purple","plum"] }; my ($color, $fruit) = ( $hoa->{keytwo}->[$idx_color], $hoa->{keytwo}-> +[$idx_fruit] ); print "$color $fruit"; # prints "purple plum"

Replies are listed 'Best First'.
Re: HOA with array slice?
by ikegami (Patriarch) on May 25, 2005 at 17:30 UTC
    my ($color, $fruit) = ( $hoa->{keytwo}->[$idx_color], $hoa->{keytwo}->[$idx_fruit] );

    is equivalent to

    my $aref = $hoa->{keytwo}; my ($color, $fruit) = ( ${$aref}[$idx_color], ${$aref}[$idx_fruit] );

    Since you're dealing with one array (@{$aref}), yes, you can use a slice:

    my $aref = $hoa->{keytwo}; my ($color, $fruit) = @{$aref}[$idx_color, $idx_fruit];

    or back in one line:

    my ($color, $fruit) = @{$hoa->{keytwo}}[$idx_color, $idx_fruit];

    Notice I used <sigil>{$aref}. I did so because it parallels <sigil>array exactly. $aref->[] is a shortcut for ${$aref}[] exclusively, so it's useless when dealing with array slices.

    Update: oh, I forgot to answer the second question! There's nothing wrong with HoA in general. In your situation, a hash would work nicely since you're naming your parameters anyway.

    If you go with hashes, you can take hash slices:

    my ($color, $fruit) = @{$hoa->{keytwo}}{'color', 'fruit'};

    If you go with arrays, why not use constants to speed things up slightly:

    sub IDX_COLOR () { 0 } sub IDX_FRUIT () { 1 } my ($color, $fruit) = @{$hoa->{keytwo}}[IDX_COLOR, IDX_FRUIT];

    or

    use constant IDX_COLOR => 0; use constant IDX_FRUIT => 1; my ($color, $fruit) = @{$hoa->{keytwo}}[IDX_COLOR, IDX_FRUIT];

    The combination of the empty prototype and the constant value being returned makes IDX_COLOR and IDX_FRUIT constants rather than functions.

Re: HOA with array slice?
by davido (Cardinal) on May 25, 2005 at 17:31 UTC

    Something like this ought to work:

    my( $color, $fruit ) = @{$hoa->{keytwo}}[$idx_color, $idx_fruit];

    There's nothing wrong with your use of a HoA. It is true, however, that a lot of thought should go into selecting the construction of complex datastructures. The right layout makes all the difference in code complexity/simplicity.


    Dave

      There's nothing wrong with your use of a HoA. It is true, however, that a lot of thought should go into selecting the construction of complex datastructures. The right layout makes all the difference in code complexity/simplicity.

      This is even more true when you try creating an artificial neural network in pure perl...  :)  Except in NN's it is normally three, or more, levels deep (AoHoH, HoHoH, etc.) and the code to transverse such structures can get a little intresting.

Re: HOA with array slice?
by tlm (Prior) on May 25, 2005 at 21:02 UTC

    I think that the line

    my ($idx_color, $idx_fruit) = (0,1);
    is a dead giveaway that you should be using an HoH (or even an AoH). By naming your indices you are inadvertently revealing a secret desire to use meaningfully named keys instead. Go for it:
    my $hoh = { keyone => { color => 'green', fruit => 'apple' }, keytwo => { color => 'purple', fruit => 'plum' } };
    ...and here's your slice:
    my ( $color, $fruit ) = @{ $hoh->{ keytwo } }{ qw( color fruit ) };
    Actually, since you are using such nondescript names for your first keys, maybe an AoH is most suitable:
    my $aoh = [ { color => 'green', fruit => 'apple' }, { color => 'purple', fruit => 'plum' }, ]; my ( $color, $fruit ) = @{ $aoh->[ 1 ] }{ qw( color fruit ) };
    Granted, come initialization time it gets annoying to re-type the names of the keys so many times. Here's one possible way to avoid that, again featuring slices galore:
    my @fields = qw( color fruit ); my $aoh; @{ $aoh->[ @$aoh ] }{ @fields } = @$_ for ( [ qw( green apple ) ], [ qw( purple plum ) ] );

    the lowliest monk

      And that, of course, brings you to OOP...which may be better or worse depending on your viewpoint :)
      package Fruit; sub new { my $class = shift; my $self = {}; my $color = shift || ''; my $name = shift || ''; bless $self, $class; $self->color( $color ); $self->name( $name ); return $self; } sub color { my $self = shift; $self->{color} = $_[0] if $_[0]; return $self->{color}; } sub name { my $self = shift; $self->{name} = $_[0] if $_[0]; return $self->{name}; } sub print { my $self = shift; print "I am just a ", $self->color(), " ", $self->name(), "\n"; } 1; package main; my $first_fruits = Fruit->new( 'green', 'apple' ); my $second_fruits = Fruit->new( 'purple', 'plum' ); my @fruit_basket = ( $first_fruits, $second_fruits ); $second_fruits->print();
      This is a fairly basic implementation, you can get as fancy as you'd like.
      I think tlm is right, I should be using HOH here, so I will be doing a bit of code rewrite.

      One disadvantage of HOH that occurs to me right away though, is if I mistype a key when setting a value in the hash, I don't get any type of error message. I often have autovivification errors that arise in this kind of way.

      Is there any way to program defensively against this kind of error? One thing that occurs to me is I could just use the "functions as constants" thing that ikegami mentions earlier. Anyone else have other defense programming suggestions for dealing with this and autovivification with HOH?

        Several of the replies to this node address the issue you bring up.

        the lowliest monk

Re: HOA with array slice?
by TedPride (Priest) on May 25, 2005 at 22:05 UTC
    Not sure what you're trying to do here, but it looks like you just want a simple way to access the contents of the array inside your hash. All you have to do is assign a variable to reference the array:
    use warnings; use strict; my $hoa = { keyone => ["green","apple"], keytwo => ["purple","plum"] }; my $p; for (sort keys %$hoa) { $p = $hoa->{$_}; print join ' ', @$p; print "\n"; }
    You could also use:
    print join ' ', @$p[0], @$p[1]; print "\n";
    or:
    print join ' ', $p->[0], $p->[1]; print "\n";
    or just:
    for (sort keys %$hoa) { print join ' ', @{$hoa->{$_}}; print "\n"; }