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:
||=[]
. 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.)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?
||=
: 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
Let's try to deconstruct this step-by-step:
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 ] );
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).
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}}, $.;
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With