The correct way to play with data references in Pe

2019-06-26 08:09发布

问题:

I have a set of data that I would like to process.

In order to simplify my code, It would be nice to access to some subsets of my data through array of references that point to the original data.

Better than an explanation, I wrote down this example (which is not working yet). At the end, I would like to update the original data without having to also update all the subsets.

Is it possible to do something like this with Perl ?

#!/usr/bin/env perl
use strict;
use warnings;

# A set of data
my $design = {
    box => {
        ne =>  {data => 'north-east'},
        nw =>  {data => 'north-west'},
        n  =>  {data => 'north'},
        s  =>  {data => 'south'},
        e  =>  {data => 'east'},
        w  =>  {data => 'west'},
        se  => {data => 'south-east'},
        sw  => {data => 'south-west'}
    }
};

# Select a design
my $selected = 'box';

# Build some arrays
my $d = $design->{$selected};

my @eastside  = (\$d->{e}, \$d->{ne}, \$d->{se});
my @westside  = (\$d->{w}, \$d->{nw}, \$d->{sw});
my @northside = (\$d->{n}, \$d->{ne}, \$d->{nw});

# Update one data
$d->{ne}->{data} .= " updated!";

# Display
print join '', "Composed of:\n", map("\t".$_->{data}."\n", @eastside);

The script should output:

Composed of:
   east
   north-east updated!
   south-east

回答1:

All that is wrong is that you are taking a reference of values that are already hash references in the lines like

my @eastside  = (\$d->{e}, \$d->{ne}, \$d->{se})

You should simple omit the backslashes and everything will work.

(By the way, you may want to know that the reference \ operator is distributive, so you can write the same thing with

my @eastside  = \( $d->{e}, $d->{ne}, $d->{se} )

but it's no more correct that way!)

Some other points

  • You should make use of hash slices in situations like this where you need to extract a list of hash elements using multiple keys. In this case @eastside is just @{$d}{qw/ ne e se /}

  • Perl allows the indirection operator -> between pairs of closing and opening braces and brackets to be omitted, so $d->{ne}->{data} can be written $d->{ne}{data}

  • You are printing the result of a join with a null between the elements. You'll get the same result by just listing the items to be printed. You can also interpolate hash elements into a double-quoted string, so "\t".$_->{data}."\n" is the same as "\t$_->{data}\n"

Making those changes results in this working program

use strict;
use warnings;

# A set of data
my $design = {
   box => {
      ne => {data => 'north-east'},
      nw => {data => 'north-west'},
      n  => {data => 'north'},
      s  => {data => 'south'},
      e  => {data => 'east'},
      w  => {data => 'west'},
      se => {data => 'south-east'},
      sw => {data => 'south-west'},
   }
};

# Select a design
my $selected = 'box';

# Build some arrays
my $d = $design->{$selected};

my @eastside  = @{$d}{qw/ ne e se /};
my @westside  = @{$d}{qw/ nw w sw /};
my @northside = @{$d}{qw/ nw n ne /};

# Update one item
$d->{ne}{data} .= " updated!";

# Display
print "Composed of:\n"; 
print "   $_->{data}\n" for @eastside;

output

Composed of:
   north-east updated!
   east
   south-east


回答2:

You have scalar references in @eastside array, so in order to dereference scalar put extra $ in front of $_->{data}, or use ${$_}->{data}.

print join '', "Composed of:\n", map("\t".$$_->{data}."\n", @eastside);

output

Composed of:
    east
    north-east updated!
    south-east


回答3:

You can check the data structure with Data::Dumper module.

Also check the next two @east2 and @east3 examples, especially how is built the @east3.

#!/usr/bin/env perl
use 5.010;
use warnings;
use Data::Dumper;

# A set of data
my $design = {
    box => {
        ne =>  {data => 'north-east'}, nw =>  {data => 'north-west'},
        n  =>  {data => 'north'}, s  =>  {data => 'south'},
        e  =>  {data => 'east'}, w  =>  {data => 'west'},
        se  => {data => 'south-east'}, sw  => {data => 'south-west'}
    }
};
my $selected = 'box';

my $d = $design->{$selected};

#your example
my @east1  = (\$d->{e}, \$d->{ne}, \$d->{se});
say Dumper \@east1;

my @east2  = ($d->{e}, $d->{ne}, $d->{se});
say Dumper \@east2;

my @east3 = @$d{qw(e ne se)};
say Dumper \@east3;

$d->{ne}->{data} .= " updated!";

print join '', "Composed of:\n", map("\t".$$_->{data}."\n", @east1);

print join '', "Composed of:\n", map("\t".$_->{data}."\n", @east2);
print join '', "Composed of:\n", map("\t".$_->{data}."\n", @east3);

prints:

$VAR1 = [
          \{
              'data' => 'east'
            },
          \{
              'data' => 'north-east'
            },
          \{
              'data' => 'south-east'
            }
        ];

$VAR1 = [
          {
            'data' => 'east'
          },
          {
            'data' => 'north-east'
          },
          {
            'data' => 'south-east'
          }
        ];

$VAR1 = [
          {
            'data' => 'east'
          },
          {
            'data' => 'north-east'
          },
          {
            'data' => 'south-east'
          }
        ];

Composed of:
    east
    north-east updated!
    south-east
Composed of:
    east
    north-east updated!
    south-east
Composed of:
    east
    north-east updated!
    south-east