Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding pushing to a hash and ||=[] construct. (generated in boilerplate.t)

I'm re-acquainting myself with Perl, and have just used module-starter to initialise a new project. I'm now trying to understand the generated code. All is fine apart from the follow line indicated :

sub not_in_file_ok {
  my ($filename, %regex) = @_;
  open( my $fh, '<', $filename )
    or die "couldn't open $filename for reading: $!";

  my %violated;

  while (my $line = <$fh>) {
    while (my ($desc, $regex) = each %regex) {
        if ($line =~ $regex) {
            ##I'm having problems here
            push @{$violated{$desc}||=[]}, $.;
        }
    }
  }
  ...
}

I have two problems:

  1. The ||=[]. Is this | followed by |=, or is this an or || followed by an =[]. Can someone talk me through what is happening here? (I'm guessing "if the hash is empty the create an empty anonymous array to initialise the hash", but I'm struggling to see how that is formed from the code.)
  2. push @{$violated{$desc}}, $. I understand this to mean "assign the line number to the key $desc for the hash %violated. But from the code I read, "lookup the value of the key desc of $violated{$desc} (the $violated{$desc} part), then use this value as a symbolic reference to an array (the @{$value} part), then push the line number onto that array". I don't see how to reconcile these two views.

I think there is a lot for me to learn in this line of code - can someone help me by walking me through it?

like image 703
Tom Avatar asked Nov 21 '11 14:11

Tom


2 Answers

  • ||=: this is an assignment operator. Example

    $a ||= $b;
    # corresponds to
    $a = $a || $b;
    

    see man perlop. In your example

    $a ||= [];
    # corresponds to
    $a = $a || [];
    

    that is: if the left operand is defined to nothing, otherwise assign an empty array reference

  • %violated contains an array reference for each value. You can see it like that:

    my $array_ref = $violated{$desc};
    push @{array_ref}, $.;
    

Written more verbosely:

  if (! $violated{$desc} ) {
      $violated{$desc} = [];
  }
  my $array_ref = $violated{$desc};
  push @{ $array_ref }, $.;

EDIT

Arrays and array references

  • an array constructed with () and contains a dynamic ordered list of elements (in Perl arrays can grow dynamically)

  • an array reference is a reference to an array (more or less a pointer without pointer arithmetic). You can create and array reference with []

Example

my @a = ( 1, 2, 3);
# $a[0] will contain 1

my $array_ref = [ 10, 11, 12 ];
# array_ref is a _pointer_ to an array containing 10, 11 and 12

To access an array reference you need to dereference it:

@{ $array_ref };

my @array = @{ $array_ref }; # is valid

You can access { $array_ref} as an array

${ $array_ref }[0]

Now back to your question in the comment: %violated is an hash with the following key-value pairs: a string ($desc) and an array reference

like image 188
Matteo Avatar answered Sep 21 '22 09:09

Matteo


Let's try to deconstruct this step-by-step:

  1. The line is used to populate a hash of arrayrefs, where the arrayrefs contain the line numbers where the $desc regex matches. The resultant %violated hash will look something like:

    ( desc1 => [ 1, 5, 7, 10 ], desc2 => [ 2, 3, 4, 6, 8 ] );

  2. push takes an array as its first argument. The variable $violated{$desc is an arrayref, not an array, so the @{...} is used to dereference it (dereferencing is the opposite of referencing).

  3. Now for the tricky part. The stuff inside the braces is just a fancy way of saying that if $violated{$desc} is not defined inside %violated (tested with ||), it is assigned (=) to an empty arrayref ([]). Think of it as two assignments in one line:

    $violated{$desc} = $violated{$desc} || [];

    push @{$violated{$desc}}, $.;

  4. Note that this complication isn't usually necessary, thanks to a feature called autovivification, which automatically creates previously undefined keys inside the hash with the intended context (an arrayref in this case). The only case I can think of where this would be needed is if $violated{$desc} == 0 before.

like image 41
Zaid Avatar answered Sep 21 '22 09:09

Zaid