Perl significance of $#_ variable - perl

I see when I loop through elements of an array, and test $#_ , I get -1 for each element. I am hoping someone can explain what this variable does, and what it is used for most often.

Just like $#foo is the last existing index of array #foo, $#_ is the last existing index of array #_. If #_ is empty, $#_ is -1.
It sounds like you mean to use $_. $_ is aliased by foreach, map and grep loops to the element current being processed. while (<>) also sets $_ (as it gets rewritten to while (defined($_ = <>))). As a result, $_ is used as the default argument by many builtins (e.g. say).
# Print each element on its own line
say for #a;
is short for
# Print each element on its own line
say $_ for #a;
which is the terse form of
# Print each element on its own line
for my $ele (#a) {
say $ele;
}

I believe you mean $_ which is a special variable in Perl. It holds the current value while looping through a list element. For instance, below will print out each element of #foo, one at a time.
foreach (#foo) {
print $_;
}

Related

How to make initialized value with in #array in regrep compilation

i try to run this code lines,
#checkpoint = split (/\s+/,$array_lcp[0]);
$i=scalar #checkpoint
print NAME " $checkpoint[0] ";
for ($k=0; $k<=i; $k++)
{
if ($array_ARGVTEMP[$d] =~ m/$checkpoint[$k]/i)
{
#array = split (/\s+/,$array_ARGVTEMP[$d]);
print NAME " $checkpoint[$k]| $k||
$checkpoint[0]||| $checkpoint[1] ||||$checkpoint[2]||||| ";
} }
but in the result warnings, it said that:
"Use of uninitialized value within #checkpoint in regexp compilation at new3.pl line 64 (#2)" and line 64 is " if($array_ARGVTEMP[$d] =~ m/$checkpoint[$k]/i) "
Please help me, Thank you
#checkpoint has $i elements numbered 0 to $i-1, but you're accessing the element at index $i.
for (my $k=0; $k<=$i; $k++)
should be
for (my $k=0; $k<$i; $k++)
Actually, it should be
for my $k (0..$#checkpoints)
Actually, it should be
for my $checkpoint (#checkpoints)
Update The question changed. Originally the loop went to $k<=2 (array size wasn't mentioned), what this answer addressed. The main point remains and now it is clear that the loop goes up to the index equal to the array size, so one past the fence; the limit should be $k < $i. Thus "value of $k for which there is no element" mentioned below is the last one looped over.
The $checkpoint[$k], that draws the warning in the regex, are elements of the array #checkpoint with indices 0, 1, 2 -- what $k is in the loop.
The "uninitialized value within..." means that the array #checkpoint doesn't actually have all those elements, so for a value of $k for which there is no element the regex attempts to retrieve an undefined value and complains.
The first split likely returned fewer than three elements. Print out the #checkpoint to see.
A few more comments
Please always have use warnigs; and use strict; at the beginning of a program
Use lexical filehandles, so open files with open my $name, ... (not open NAME, ...)
To loop over numbers from a range a nice way is
for my $k (0..2) { ... }
(update) ... but the question changed, with the loop (intended to be) over all array elements and then there is no reason to use the index. Iterate directly over elements
foreach my $checkpoint (#checkpoints) { .... }
Whenever you use \s+ for the separator pattern in split most likely you should be using a special pattern ' ', which splits by \s+ and also disregards leading and trailing space.

Handling with two warnings of #ARGS

Small debug question I can't solve for some reason. Consider the following code:
use warnings;
my $flag = 0;
foreach my $i (0..scalar(#ARGV)) {
$data{$OPTION} .= $ARGV[$i]." " if($flag);
$flag = 1 if($ARGV[$i] =~ /$OPTION/);
undef $ARGV[$i] if($flag);
}
I get the following two warnings:
Use of uninitialized value within #ARGV in concatenation (.) or string at line 4
Use of uninitialized value in pattern match (m//) at line 5
I get the reason is that I undefine some value of #ARGV and then it tries to check it.
The way I do it like this is because I would like to 'cut' some of the data of #ARGV before using GetOpt module (which uses this array).
How to solve it?
Let's expand on those comments a bit.
Imagine #ARGV contains four elements. They will have the indexes 0, 1, 2 and 3 (as arrays in Perl are zero-based).
And your loop looks like this:
foreach my $i (0..scalar(#ARGV)) {
You want to visit each element in #ARGV, so you use the range operator (..) to generate a list of all those indexes. But scalar #ARGV returns the number of elements in #ARGV and that's 4. So your range is 0 .. 4. And there's no value at $ARGV[4] - so you get an "undefined value" warning (as you're trying to read past the end of an array).
A better way to do this is to use $#ARGV instead of scalar #ARGV. For every array variable in Perl (say #foo) you also get a variable (called $#foo) which contains the last index number in the array. In our case, that's 3 and your range (0 .. $#ARGV) now contains the integers 0 .. 3 and you no longer try to read past the end of the array and you don't get the "undefined value" warnings.
There's one other improvement I would suggest. Inside your loop, you only ever use $i to access an element from #ARGV. It's only used in expressions like $ARGV[$i]. In this case, it's probably better to skip the middle man and to iterate across the elements in the array, not the indexes.
I mean you can write your code like this:
foreach my $arg (#ARGV) {
$data{$OPTION} .= $arg . " " if($flag);
$flag = 1 if($arg =~ /$OPTION/);
undef $arg if($flag);
}
I think that's a little easier to follow.

Explanation of Perl's syntax from module MoreUtils.pm

I am seeking explanation of the syntax of Perl's uniq and fidrstidx function from module MoreUtils.pm.
Having sought that, I already know other ways to get uniq array elements from an array having duplicate elements and finding the first index from an array by below ways :
## remove duplicate elements ##
my #arr = qw (2 4 2 8 3 4 6);
my #uniq = ();
my %hash = ();
#uniq = grep {!$hash{$_}++ } #arr;
### first index ###
#arr = qw (Java ooperl Ruby cgiperl Python);
my ($index) = grep {$arr[$_] =~ /perl/} 0..$#arr;
Can anybody please explain me second line of this below sub uniq function comprising map and ternary operator from MoreUtils.pm:
map {$h{$_}++ == 0 ? $_ : () } #;
and also
the &# passed to firstidx function and the below line in the body of the function :
local *_ = \$_[$i];
What I understand that sub routine ref is passed to firstidx. But a bit more detailed explanation will be much appreciated.
Thanks.
Your second question was answered in the comments.
Your first question asks about map {$h{$_}++ == 0 ? $_ : () } #; from List::MoreUtils. In recent versions, it's actually in List::MoreUtils::PP (for Pure Perl) since many of the subroutines are also implemented in C and XS. Here's the current version of the Pure Perl uniq:
sub uniq (#)
{
my %seen = ();
my $k;
my $seen_undef;
grep { defined $_ ? not $seen{ $k = $_ }++ : not $seen_undef++ } #_;
}
This has the same map technique although it's using grep instead. The grep goes through all of the elements in #_ and has to return either true or false for each of them. The elements which evaluate to true end up in the output list. The code then wants to make an element evaluate to true the first time it sees it and false the rest of the times.
In this code it handles undef separately. If the current element is not undef, it does the first branch of the conditional operator and the second branch otherwise. Now let's look at the branches.
The defined case adds an element to a hash. No one left code comments about the use of $k but it probably has something to do with not disturbing $_. That $k becomes the key for the hash:
not $seen{ $k = $_ }++
If that is the first time that key has been encountered the value of the hash is undef. That post-increment does its work after the value is used so hold off on thinking about that for a moment. The low-precendence not sees the value of $seen{$k}, which is undef. The not turns the false value of undef into true. That true indicates that the grep has seen $_ for the first time. It becomes part of the output list. Then the ++ does its work and increments the undef value to 1. On all subsequent encounters with the same value the hash value will be true. The not will turn the true value into false and that element won't be in the output list.
The map you show implements the grep. It returns an element when the condition is true and returns no elements when it is false:
map {$h{$_}++ == 0 ? $_ : () } #_;
For each element it adds it as the key in the hash and compares the value to 0. The first time an element is seen that value is undef. In numeric context an undef is 0. So, the == returns true and the first branch of the conditional operator fires, returning $_ to the output list. The ++ then increments the hash value from undef to 1. The next time it encounters the same value the hash value is not 0 and the second branch of the conditional operator returns the empty list. That adds no elements to the output list.
Newer version of List::MoreUtils don't use the construct any more, but as Сухой27 explained,
map { CONDITION ? $_ : () } LIST
is just a fancy alternative to
grep { CONDITION } LIST
I don't think there's any overarching reason the author chose map for this implementation, and in fact it was simplified to grep in later versions of List::MoreUtils.
The firstidx syntax is firstidx BLOCK LIST. Like the builtin map and grep, it is specified that the code in BLOCK will operate on the variable $_, and that the code is allowed to make changes to $_. So in the firstidx implementation, it is not sufficient to set $_ to each value in LIST. Rather, $_ must be aliased to each element of LIST so that a change in $_ inside BLOCK also results to a change in the element in the LIST. This is accomplished by manipulating the symbol table
local *_ = \$scalar # make $_ an alias of $scalar
And you use local so that when firstidx is done, we haven't clobbered any useful information that was previously in the $_ variable.

Why can't I assign a variable to a hash entry in Perl?

Sorry, I'm super rusty with Perl. See the following code:
foreach my $hash (keys %greylist)
{
$t = $greylist{$hash};
print $greylist{$hash}[4] . "\n";
print $t[4] . "\n";
}
Why does $t[4] evaluate to a blank string, yet $greylist{$hash}[4] which should be the same thing evaluates to an IP address?
$greylist{$hash} contains an array reference. When you do:
print $greylist{$hash}[4];
Perl automatically treats it as an array reference but when you do:
$t = $greylist{$hash};
print $t[4];
You're assigning the array reference to a scalar variable, $t, then attempting to access the 5th element of another variable, #t. use strict would give you an error in this scenario.
Use the arrow operator, ->, to dereference:
$t = $greylist{$hash};
print $t->[4];
perlreftut has a note about this:
If $aref holds a reference to an array, then $aref->[3] is the fourth element of the array. Don't confuse this with $aref[3] , which is the fourth element of a totally different array, one deceptively named #aref . $aref and #aref are unrelated the same way that $item and #item are.

Grep to find item in Perl array

Every time I input something the code always tells me that it exists. But I know some of the inputs do not exist. What is wrong?
#!/usr/bin/perl
#array = <>;
print "Enter the word you what to match\n";
chomp($match = <STDIN>);
if (grep($match, #array)) {
print "found it\n";
}
The first arg that you give to grep needs to evaluate as true or false to indicate whether there was a match. So it should be:
# note that grep returns a list, so $matched needs to be in brackets to get the
# actual value, otherwise $matched will just contain the number of matches
if (my ($matched) = grep $_ eq $match, #array) {
print "found it: $matched\n";
}
If you need to match on a lot of different values, it might also be worth for you to consider putting the array data into a hash, since hashes allow you to do this efficiently without having to iterate through the list.
# convert array to a hash with the array elements as the hash keys and the values are simply 1
my %hash = map {$_ => 1} #array;
# check if the hash contains $match
if (defined $hash{$match}) {
print "found it\n";
}
You seem to be using grep() like the Unix grep utility, which is wrong.
Perl's grep() in scalar context evaluates the expression for each element of a list and returns the number of times the expression was true.
So when $match contains any "true" value, grep($match, #array) in scalar context will always return the number of elements in #array.
Instead, try using the pattern matching operator:
if (grep /$match/, #array) {
print "found it\n";
}
This could be done using List::Util's first function:
use List::Util qw/first/;
my #array = qw/foo bar baz/;
print first { $_ eq 'bar' } #array;
Other functions from List::Util like max, min, sum also may be useful for you
In addition to what eugene and stevenl posted, you might encounter problems with using both <> and <STDIN> in one script: <> iterates through (=concatenating) all files given as command line arguments.
However, should a user ever forget to specify a file on the command line, it will read from STDIN, and your code will wait forever on input
I could happen that if your array contains the string "hello", and if you are searching for "he", grep returns true, although, "he" may not be an array element.
Perhaps,
if (grep(/^$match$/, #array)) more apt.
You can also check single value in multiple arrays like,
if (grep /$match/, #array, #array_one, #array_two, #array_Three)
{
print "found it\n";
}