Why does this code print only the first element? - perl

Can anyone explain to me why only the first item of this array is being printed?
print "Hello World!\n";
#array1 = ("john","haider","elley","uul");
#array2 = qw/This is an array/;
$array_size=scalar #array1;
print "Size: ",$array_size,"\n";
$i;
for($i=0; $i<=$array_size; $i=$i+1)
{
print"Array elements are=",$array1[i],"\n";
}

Because you've typo'ed here: $array1[i].
That should be $i, as in $array1[$i] - and this is something that use strict; use warnings would have warned you about. Bareword "i" not allowed while "strict subs" in use
But as it stands - because you use i there, and it's not a variable - it's evaluated as zero, so you just print the first element of the array.
A better idiom would be:
foreach my $value ( #array1 ) {
print "Array elements are=", $value,"\n";
}
It is rarely necessary (or desirable) to manipulate arrays by index in perl.

Related

How foreach loop in perl works with arrays in perl

this program should execute three times but is executing only twice.
Can anyone explain how this foreach loop will work in perl.
#!/usr/bin/perl
use strict;
use warnings;
my #arr=("sandeepr", "vijay","vikas");
for my $i( #arr)
{
print #arr;
my $b=pop(#arr);
print "\n $b";
}
perlsyn:
If any part of LIST is an array, foreach will get very confused if you add or remove elements within the loop body, for example with splice. So don't do that.
As confused as this makes Perl, you appear to be even more confused. What are trying to do? Print the elements in reverse order? If so, you could use
for my $ele (reverse #arr) {
print("$ele\n");
}
or
for my $i (1..#arr) {
my $ele = $arr[-$i];
print("$ele\n");
}
or
while (#arr) {
my $ele = pop(#arr);
print("$ele\n");
}

Perl - Error when trying to access two-digit number in array within foreach-loop

I just started learning Perl and stumbled across an error that I couldnt solve by googling or reading the book I'm learning with.
My code looks like this:
use strict;
use warnings;
my #array = (1, 2, 10);
foreach my $i (#array) {
print $array[$i-1];
}
This works well if I have an array consisting only of single-digit numbers. As soon as the foreach-loop reaches a double-digit number (10 in this case), I get a warning saying "Use of uninitialized value in ..." and the loop stops. The same happens if I try to do anything with the numbers (addition etc.), it works well with single-digits, but stops at double-digits.
I'm now having trouble understanding what's happening here - I guess it has anything to do with the way that Perl stores the elements of the array, but I can't find anything and don't really know what to search for.
Can anyone point me in the right direction?
I'm not sure what you're trying to do, but this doesn't do what you think it does.
my #array = (1, 2, 10);
foreach my $i (#array) {
print $array[$i-1];
}
$i is not an index like 0, 1, 2, 3... Instead $i is each element of #array: 1, 2, 10. So $array[$i-1] is $array[0], $array[1], $array[9]. #array has no ninth element.
If you want to loop through each index of #array, loop like this:
for my $idx (0..$#array) {
# 0, 1, 2
print "$idx\n";
}
If you want to loop through each element of #array, loop like this:
for my $element (#array) {
# 1, 2, 10
print "$element\n";
}
While you work with a book I'd like to suggest to also use the excellent Perl documentation. Here are some examples in the context of an answer. While at it I'll also suggest a few other niceties.
From Foreach loops (perlsyn page)
The foreach loop iterates over a normal list value and sets the scalar variable VAR to be each element of the list in turn.
Thus it picks each element from an array
use warnings;
use strict;
use feature 'say';
my #words = qw(one two three);
foreach my $word (#words) {
say $word;
}
The say builtin is like print but adds the newline, see feature pragma. The qw operator builds a list of "words" (separated by spaces) which is assigned to array #words. It is often very convienent.
If you only want to print an array, the statement modifiers (perlsyn) are handy
say for #array;
This also makes use of the $_ variable, which is a default for many things in Perl. So the above is same as say $_ for #array.
If you'd rather iterate over array indices that can be done nicely using the range operator
foreach my $index (0 .. $#array) {
say "$array[$index] at index $index";
}
where $#array is a bit special notation for the subscript (index) of the last element of #array. Find it under Scalar values in perldata. When range is used in foreach loops there is no temporary array built, so it is memory efficient. While it can also be used to make an array, my #nums = 1..10;
The code in the question iterates over elements but then uses them to index into the array. So it does print $array[ $element ]. This runs without trouble only accidentally when the elements happen to be numbers, and which happen to be valid indices for that particular array.
In the Perl foreach you could access each element directly from the iteration variable, in your case it's $i.
Try the following:
#!/usr/bin/perl
use strict;
use warnings;
my #array = (1, 2, 10);
foreach my $i (#array) {
print $i."\n";
}
Note the ."\n" in the print function call. It's not needed, I've added that just to beautify the output.
Both previous answers are good. I'll just add another slight variation to the mix.
#!/usr/bin/perl
use strict;
use warnings;
my #array = (6, 1, 2, 10);
my $numelements = #array;
for (my $i=0; $i < $numelements; $i++) {
print "$array[$i]\n";
}
I'd like to mention the each function. You might now think that you have to choose between iterating over the index of the array and the values of the array:
Iterating over the index:
foreach my $index (0 .. $#array) {
print "$index\n"; # 0, 1, 2
}
Iterating over the values:
foreach my $value (#array) {
print "$value\n"; # 1, 2, 10
}
But you can have both at the same time if you have at least Perl 5.12. Use the each function:
while (my ($index, $value) = each #array) {
print "index=$index, value=$value\n";
}
Output:
index=0, value=1
index=1, value=2
index=2, value=10
This can be useful if you want to iterate over an array and want to examine the previous or next element, for example.

Opposite of ~~ in Perl

Is there an opposite for the operator ~~ in Perl? I used it to match an element in an array like this:
my #arr = qw /hello ma duhs udsyyd hjgdsh/;
print "is in\n" if ('duhs' ~~ #arr);
This prints is in. It is pretty cool, because I don't have to iterate all the array and compare each record. My problem is that I want to do something if I don't have a match. I could go on the else side but I rather find a opposite for `~~'
You can also use List::Util (newer versions of this module only) or List::MoreUtils with any, none and friends.
use List::Util qw(any none);
my #arr = qw /hello ma duhs udsyyd hjgdsh/;
say "oh hi" if any { $_ eq 'hello' } #arr;
say "no goodbyes?" if none { $_ eq 'goodbye' } #arr;
While not perl-native, it doesn't need the experimental smartmatching.
unless == if not
print "is not in\n" unless ('duhs' ~~ #arr);
Note: Smart matching is experimental in perl 5.18+. See Smart matching is experimental/depreciated in 5.18 - recommendations? So use the following instead:
print "is not in\n" unless grep { $_ eq 'duhs' } #arr;

Why does my Perl max() function always return the first element of the array?

I am relatively new to Perl and I do not want to use the List::Util max function to find the maximum value of a given array.
When I test the code below, it just returns the first value of the array, not the maximum.
sub max
{
my #array = shift;
my $cur = $array[0];
foreach $i (#array)
{
if($i > $cur)
{
$cur = $i;
}
else
{
$cur = $cur;
}
}
return $cur;
}
Replace
my #array = shift;
with
my #array = #_;
#_ is the array containing all function arguments. shift only grabs the first function argument and removes it from #_. Change that code and it should work correctly!
Why don't you want to use something that works?
One of the ways to solve problems like this is to debug your data structures. At each step you print the data you have to see if what you expect is actually in there. That can be as simple as:
print "array is [#array]\n";
Or for complex data structures:
use Data::Dumper;
print Dumper( \#array );
In this case, you would have seen that #array has only one element, so there it must be the maximum.
If you want to see how list assignment and subroutine arguments work, check out Learning Perl.
You can write the function as:
#!/usr/bin/perl
use strict; use warnings;
print max(#ARGV);
sub max {
my $max = shift;
$max >= $_ or $max = $_ for #_;
return $max;
}
However, it would be far more efficient to pass it a reference to the array and even more efficient to use List::Util::max.

Automatically get loop index in foreach loop in Perl

If I have the following array in Perl:
#x = qw(a b c);
and I iterate over it with foreach, then $_ will refer to the current element in the array:
foreach (#x) {
print;
}
will print:
abc
Is there a similar way to get the index of the current element, without manually updating a counter? Something such as:
foreach (#x) {
print $index;
}
where $index is updated like $_ to yield the output:
012
Like codehead said, you'd have to iterate over the array indices instead of its elements. I prefer this variant over the C-style for loop:
for my $i (0 .. $#x) {
print "$i: $x[$i]\n";
}
In Perl prior to 5.10, you can say
#!/usr/bin/perl
use strict;
use warnings;
my #a = qw/a b c d e/;
my $index;
for my $elem (#a) {
print "At index ", $index++, ", I saw $elem\n";
}
#or
for my $index (0 .. $#a) {
print "At index $index I saw $a[$index]\n";
}
In Perl 5.10, you use state to declare a variable that never gets reinitialized (unlike ones created with my). This lets you keep the $index variable in a smaller scope, but it can lead to bugs (if you enter the loop a second time it will still have the last value):
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
my #a = qw/a b c d e/;
for my $elem (#a) {
state $index;
say "At index ", $index++, ", I saw $elem";
}
In Perl 5.12 you can say
#!/usr/bin/perl
use 5.012; # This enables strict
use warnings;
my #a = qw/a b c d e/;
while (my ($index, $elem) = each #a) {
say "At index $index I saw $elem";
}
But be warned: you there are restrictions to what you are allowed to do with #a while iterating over it with each.
It won't help you now, but in Perl 6 you will be able to say
#!/usr/bin/perl6
my #a = <a b c d e>;
for #a Z 0 .. Inf -> $elem, $index {
say "at index $index, I saw $elem"
}
The Z operator zips the two lists together (i.e. it takes one element from the first list, then one element from the second, then one element from the first, and so on). The second list is a lazy list that contains every integer from 0 to infinity (at least theoretically). The -> $elem, $index says that we are taking two values at a time from the result of the zip. The rest should look normal to you (unless you are not familiar with the say function from 5.10 yet).
perldoc perlvar does not seem to suggest any such variable.
It can be done with a while loop (foreach doesn't support this):
my #arr = (1111, 2222, 3333);
while (my ($index, $element) = each(#arr))
{
# You may need to "use feature 'say';"
say "Index: $index, Element: $element";
}
Output:
Index: 0, Element: 1111
Index: 1, Element: 2222
Index: 2, Element: 3333
Perl version: 5.14.4
Not with foreach.
If you definitely need the element cardinality in the array, use a 'for' iterator:
for ($i=0; $i<#x; ++$i) {
print "Element at index $i is " , $x[$i] , "\n";
}
No, you must make your own counter. Yet another example:
my $index;
foreach (#x) {
print $index++;
}
when used for indexing
my $index;
foreach (#x) {
print $x[$index]+$y[$index];
$index++;
}
And of course you can use local $index; instead my $index; and so and so.
autobox::Core provides, among many more things, a handy for method:
use autobox::Core;
['a'..'z']->for( sub{
my ($index, $value) = #_;
say "$index => $value";
});
Alternatively, have a look at an iterator module, for example: Array::Iterator
use Array::Iterator;
my $iter = Array::Iterator->new( ['a'..'z'] );
while ($iter->hasNext) {
$iter->getNext;
say $iter->currentIndex . ' => ' . $iter->current;
}
Also see:
each to their own (autobox)
perl5i
Yes. I have checked so many books and other blogs... The conclusion is, there isn't any system variable for the loop counter. We have to make our own counter. Correct me if I'm wrong.
Oh yes, you can! (sort of, but you shouldn't). each(#array) in a scalar context gives you the current index of the array.
#a = (a..z);
for (#a) {
print each(#a) . "\t" . $_ . "\n";
}
Here each(#a) is in a scalar context and returns only the index, not the value at that index. Since we're in a for loop, we have the value in $_ already. The same mechanism is often used in a while-each loop. Same problem.
The problem comes if you do for(#a) again. The index isn't back to 0 like you'd expect; it's undef followed by 0,1,2... one count off. The perldoc of each() says to avoid this issue. Use a for loop to track the index.
each
Basically:
for(my $i=0; $i<=$#a; $i++) {
print "The Element at $i is $a[$i]\n";
}
I'm a fan of the alternate method:
my $index=0;
for (#a) {
print "The Element at $index is $a[$index]\n";
$index++;
}
Please consider:
print "Element at index $_ is $x[$_]\n" for keys #x;
Well, there is this way:
use List::Rubyish;
$list = List::Rubyish->new( [ qw<a b c> ] );
$list->each_index( sub { say "\$_=$_" } );
See List::Rubyish.
You shouldn't need to know the index in most circumstances. You can do this:
my #arr = (1, 2, 3);
foreach (#arr) {
$_++;
}
print join(", ", #arr);
In this case, the output would be 2, 3, 4 as foreach sets an alias to the actual element, not just a copy.
I have tried like....
#array = qw /tomato banana papaya potato/; # Example array
my $count; # Local variable initial value will be 0.
print "\nBefore For loop value of counter is $count"; # Just printing value before entering the loop.
for (#array) { print "\n",$count++," $_" ; } # String and variable seperated by comma to
# execute the value and print.
undef $count; # Undefining so that later parts again it will
# be reset to 0.
print "\nAfter for loop value of counter is $count"; # Checking the counter value after for loop.
In short...
#array = qw /a b c d/;
my $count;
for (#array) { print "\n",$count++," $_"; }
undef $count;