Does my $_; do anything if $_ is implied - perl

I think the answer is yes but I just want to make sure. so if I have
sub something {
my $_;
my #array = ...;
while ( #array ) {
say;
}
}
is the my $_; actually effective at lexicalizing the parameter passed to the say?
In this particular case I'm using the DZP::UnusedVarsTests and it's complaining that I haven't used my $_; and I suspect it's a bug since I'm using it in a case where it's implied.

The short answer is Yes. It makes the functions in that scope use the lexically scoped $_, and not the global $_. If they write back to $_, as in the case of s///, you will have some level of damage control.
Per perldoc perldelta (5.10.0):
"List::Util::first" misbehaves in the presence of a lexical $_ (typically introduced by "my $_" or implicitly by "given"). The variable which gets set for each iteration is the package variable $_, not the lexical $_ [RT #67694].
A similar issue may occur in other modules that provide functions which take a block as their first argument, like
foo { ... $_ ...} list
And, in perldoc perl591delta it goes on to say:
Lexical $_
The default variable $_ can now be lexicalized, by declaring it like
any other lexical variable, with a
simple
my $_;
The operations that default on $_ will use the lexically-scoped version of $_ when it exists, instead of the global $_.
In a "map" or a "grep" block, if $_ was previously my'ed, then the $_ inside the block is lexical as well (and scoped to the block).
In a scope where $_ has been lexicalized, you can still have access to the global version of $_ by using $::_, or, more simply, by overriding the lexical declaration with "our $_".
Examples
I wanted to provide some examples of why this functionality would be used:
my $_ = 'BOOM!';
sub something {
my $_; ## Try running with and without
my #array = qw/foo bar baz/;
while ( $_ = pop #array ) {
say;
}
}
something();
say;
And, another example
my $_ = 'foo';
sub something {
my $_ = $_; ## Try running with and without
s/foo/bar/;
$_;
}
something();
say;

Related

Perl, Best Practices: Empty Arguments handling inside subroutine, use of $_

I ran into following code example while was reading book "Perl Best Practices by Damian Conway":
sub fix {
my (#args) = #_ ? #_ : $_; # Default to fixing $_ if no args provided
for my $arg (#args) {
print $arg;
}
return;
}
Could you please help me to understand what exactly author wants to accomplish in this part of code?
my (#args) = #_ ? #_ : $_; # Default to fixing $_ if no args provided
I understand what "if" does, and it's clear for me that we will assign $_ to "#args" when user does not provide parameters for "fix() sub"
But it's not clear what is the benefit to assign $_ to #args since $_ is undef, right?
Thank you in advance.
#_ and $_ are two different variables, so $_ can be defined even if #_ is empty. $_ is used as the default in many functions, e.g. length or chr.
See $_ and #_.

Display folders using perl script

I have the following code and my problem is that I cannot modify it in order to use the $file3 outside the for function
for ($i = 0; $i < scalar(#temp); $i++){
$path7 = 'path_to'.#temp[$i];
foreach $path ($path7){
opendir my ($dh3), $path7 or die $!;
while ( my $file3 = readdir $dh3 ) {
next if $file3 eq '.' or $file3 eq '..';
next unless -d catfile($path7, $file3);
print "$file3\n";
}
closedir ($dh3);
}
}
Your $file3 is lexical to the while loop because you declared it with my. If you want it to be available outside, declare it in a larger scope, i.e. outside the for.
my $file3; # here!
for ( ...) {
# ...
# ...
######### no my below
while ( $file3 = readdir $dh3 ) {
# ...
}
# ...
}
Remember that in Perl it's a good practice to declare variables in the smallest scope necessary.
Also note that outside the while loop it will start out being undef and after being done processing the while for the first time ($i is 0, $path is the value of $path7), $file3 will keep the value it had in the last round of the while loop until the next time the while loop starts. That is never, because your foreach's list only has one element, as $path7 is a scalar and not an array. In fact, there is no need for that foreach loop at all. Just use $path7 directly.
Confused with my explanation because of the variable names? Me too. Always pick meaningfull variable names, don't just append numbers. That makes it very hard to maintain. :)

Perl recursive code for scanning directory tree

In this script that scan a directory recursively, i would like to know what happen when the "ScanDirectory($name)" is called -> does the "next" get executed right after?
Cause if the #names gets populated with new directories after each loop then we get inside the first directory in #names, and if there is other directories there Scandirectory is called again but the other directories in the previous #names are replaced and so they are not treated by the loop? Sorry if i don't make sense.
i know there is already a module for this purpose, but i want to improve my understanding of how this loop code works so i can deal with recursive code in other situations
sub ScanDirectory {
my $workdir = shift;
my $startdir = cwd;
chdir $workdir or die;
opendir my $DIR, '.' or die;
my #names = readdir $DIR or die;
closedir $DIR;
foreach my $name (#names) {
next if ($name eq ".");
next if ($name eq "..");
if (-d $name) {
ScanDirectory($name);
next;
}
}
chdir $startdir or die;
}
ScanDirectory('.');
Is this your code?
In the subroutine you call my #names = readdir that defines a new lexically scoped variable, so each recursion will create a new instance of that variable. It might work if you use our instead of my. Variables defined with our are packaged scope which means each call will use the same #names variable. Actually not even then. You're cleaning out the previous value of the variable with your readdir.
You'll be better off using File::Find. File::Find comes with most Perl installations, so it's always available.
use strict;
use warnings;
use File::Find;
my #names;
find ( sub {
next if $_ eq "." or $_ eq "..";
push #names, $File::Find::name;
}, "."
);
This is simpler to understand, easier to write, more flexible, and much more efficient since it doesn't call itself recursively. Most of the time, you'll see this without the sub being embedded in the function:
my #names;
find ( \&wanted, ".");
sub wanted {
next if $_ eq "." or $_ eq "..";
push #names, $File::Find::name;
}
I prefer to embed the subroutine if the subroutine is fairly small. It prevents the subroutine from wandering away from the find call, and it prevents the mysterious instance of #names being used in the subroutine without a clear definition.
Okay, they're both the same. Both are subroutine references (one is called wanted and one is an anonymous subroutine). However, the first use of #names doesn't appear so mysterious since it's literally defined on the line right above the find call.
If you must write your own routine from scratch (maybe a homework assignment?), then don't use recursion. use push to push the reversed readdir into an array.
Then, pop off the items of the array one at a time. If you find a directory, read it (again in reverse) and push it onto your array. Be careful with . and ...
This is strangely-written code, especially if it is published in a book.
Your confusion is because the #names array is declared lexically, which means it exists only for the extent of the current block, and is unique to a prticular stack frame (subroutine call). So each call of scan_directory (local identifiers shouldn't really contain capital letters) has its own independent #names array which vanishes when the subroutine exits, and there is no question of "replacing" the contents.
Also, the next you're referring to is redundant: it skips to the next iteration of the #names array, which is just what would happen without it.
It would be much better written like this
sub scan_directory {
my ($workdir) = #_;
my $startdir = cwd;
chdir $workdir or die $!;
opendir my $dh, '.' or die $!;
while (my $name = readdir $dh) {
next if $name eq '.' or $name eq '..';
scan_directory($name) if -d $name;
}
chdir $startdir or die $!;
}
scan_directory('.');

Perl IF statement not matching variables in REGEX

my $pointer = 0;
foreach (#new1)
{
my $test = $_;
foreach (#chk)
{
my $check = $_;
chomp $check;
delete($new1[$pointer]) if ($test =~ /^$check/i);
}
$pointer++;
}
The if statement never matches the fact that many entries in the #new1 array do contain $check at the start of the array element (88 at least).
I am not sure it is the nested loop that is causing the problem because if i try this it also fails to match:
foreach (#chk)
{
#final = (grep /^$_/, #new1);
}
#final is empty but I know at least 88 entires for $_ are in #new1.
I wrote this code on a machine running Windows ActivePerl 5.14.2 and the top code works. I then (using a copy of #new1) compare the two and remove any duplicates (also works on 5.14.2). I did try to negate the if match but that seemed to wipe out the #new1 array (so that I didn't need to do a hash compare).
When I try to run this code on a Linux RedHat box with Perl 5.8.0 it seems to struggle with the variable matching in the REGEX. If I hard code the REGEX with an example I know is in #new1 the match works and in the first code the entry is deleted (in the second one value is inserted in #final).
The #chk array is a listing file on the web server and the #new1 array is created by opening two log files on the web server and then pushing one into the other.
I had even gone to the trouble of printing out $test and $check in each loop iteration and manually checking to see if any of the the values did match and some of them do.
It has had me baffled for days now and I have had to throw the towel in and ask for help, any ideas?
As tested by user1568538, the solution was to replace
chomp $check;
with
$check =~ s/\r\n//g;
to remove Windows-style line endings from the variable.
Since chomp removes the contents of the input record separator $/ from the end of its argument, you could also change its value:
my $pointer = 0;
foreach (#new1)
{
my $test = $_;
foreach (#chk)
{
local $/="\r\n";
my $check = $_;
chomp $check;
delete($new1[$pointer]) if ($test =~ /^$_/i);
}
$pointer++;
}
However, since $/ also affects other operations (such as reading from a file handle), perhaps it is safest to avoid changing $/ unless you are sure if it is safe. Here I limit the change to the foreach loop where the chomp occurs.
No knowing what your input data looks like, using \Q might help:
if ($test =~ /^\Q$check/i);
See quotemeta.
It is not clear what you are trying to do. However, you may be trying to only get those elements for which there is no match or vice versa. Adapt the code below for your needs
#!/usr/bin/perl
use strict; use warnings;
my #item = qw(...); # your #new?
my #check = qw(...); # your #chk?
my #match;
my #nomatch;
ITEM:
foreach my $item (#item) {
CHECK:
foreach my $check (#check) {
# uncomment this if $check should not be interpreted as a pattern,
# but as literal characters:
# $item = '\Q' . $item;
if ($item =~ /^$check/) {
push #match, $item;
next ITEM; # there was a match, so this $item is burnt
# we don't need to test against other $checks.
}
}
# there was no match, so lets store it:
push #nomatch, $item.
}
print "matched $_\n" for #matched;
print "didn't match $_" for #nomatch;
Your code is somewhat difficult to read. Let me tell you what this
foreach (#chk) {
#final = (grep /^$_/, #new1);
}
does: It is roughly equivalent to
my #final = ();
foreach my $check (#chk) {
#final = grep /^$check/, #new1;
}
which is equivalent to
my #final = ();
foreach my $check (#chk) {
# #final = grep /^$check/, #new1;
#final = ();
foreach (#new) {
if (/^$check/) {
push #final, $_;
last;
}
}
}
So your #final array gets reset, possibly emptied.

Is there a Perl idiom which is the functional equivalent of calling a subroutine from within the substitution operator?

Perl allows ...
$a = "fee";
$result = 1 + f($a) ; # invokes f with the argument $a
but disallows, or rather doesn't do what I want ...
s/((fee)|(fie)|(foe)|(foo))/f($1)/ ; # does not invoke f with the argument $1
The desired-end-result is a way to effect a substitution geared off what the regex matched.
Do I have to write
sub lala {
my $haha = shift;
return $haha . $haha;
}
my $a = "the giant says foe" ;
$a =~ m/((fee)|(fie)|(foe)|(foo))/;
my $result = lala($1);
$a =~ s/$1/$result/;
print "$a\n";
See perldoc perlop. You need to specify the e modifier so that the replacement part is evaluated.
#!/usr/bin/perl
use strict; use warnings;
my $x = "the giant says foe" ;
$x =~ s/(f(?:ee|ie|o[eo]))/lala($1)/e;
print "$x\n";
sub lala {
my ($haha) = #_;
return "$haha$haha";
}
Output:
C:\Temp> r
the giant says foefoe
Incidentally, avoid using $a and $b outside of sort blocks as they are special package scoped variables special-cased for strict.