Automatically get loop index in foreach loop in Perl - 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;

Related

Passing strings as array to subroutine and return count of specific char

I was trying to think in the right way to tackle this:
-I would to pass say, n elements array as argument to a subroutine. And for each element match two char types S and T and print for each element, the count of these letters. So far I did this but I am locked and found some infinite loops in my code.
use strict;
use warnings;
sub main {
my #array = #_;
while (#array) {
my $s = ($_ = tr/S//);
my $t = ($_ = tr/T//);
print "ST are in total $s + $t\n";
}
}
my #bunchOfdata = ("QQQRRRRSCCTTTS", "ZZZSTTKQSST", "ZBQLDKSSSS");
main(#bunchOfdata);
I would like the output to be:
Element 1 Counts of ST = 5
Element 2 Counts of ST = 6
Element 3 Counts of ST = 4
Any clue how to solve this?
while (#array) will be an infinite loop since #array never gets smaller. You can't read into the default variable $_ this way. For this to work, use for (#array) which will read the array items into $_ one at a time until all have been read.
The tr transliteration operator is the right tool for your task.
The code needed to get your results could be:
#!/usr/bin/perl
use strict;
use warnings;
my #data = ("QQQRRRRSCCTTTS", "ZZZSTTKQSST", "ZBQLDKSSSS");
my $i = 1;
for (#data) {
my $count = tr/ST//;
print "Element $i Counts of ST = $count\n";
$i++;
}
Also, note that my $count = tr/ST//; doesn't require the binding of the transliteration operator with $_. Perl assumes this when $_ holds the value to be counted here. Your code tried my $s = ($_ = tr/S//); which will give the results but the shorter way I've shown is the preferred way.
(Just noticed you had = instead of =~ in your statement. That is an error. Has to be $s = ($_ =~ tr/S//);)
You can combine the 2 sought letters as in my code. Its not necessary to do them separately.
I got the output you want.
Element 1 Counts of ST = 5
Element 2 Counts of ST = 6
Element 3 Counts of ST = 4
Also, you can't perform math operations in a quoted string like you had.
print "ST are in total $s + $t\n";
Instead, you would need to do:
print "ST are in total ", $s + $t, "\n";
where the operation is performed outside of the string.
Don't use while to traverse an array - your array gets no smaller, so the condition is always true and you get an infinite loop. You should use for (or foreach) instead.
for (#array) {
my $s = tr/S//; # No need for =~ as tr/// works on $_ by default
my $t = tr/T//;
print "ST are in total $s + $t\n";
}
Why tr///??
sub main {
my #array = #_;
while (#array) {
my $s = split(/S/, $_, -1) - 1;
my $t = split(/T/, $_, -1) - 1;
print "ST are in total $s + $t\n";
}
}

Why does this code print only the first element?

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.

understanding data structures in perl

I'm trying to understand the 'Common Mistake' section in the perldsc documentation. What is the author trying to convey when he mentions:
The two most common mistakes made in constructing something like an array of arrays is either accidentally counting the number of elements or else taking a reference to the same memory location repeatedly. Here's the case where you just get the count instead of a nested array:
for my $i (1..10) {
my #array = somefunc($i);
$AoA[$i] = #array; # WRONG!
}
From what I understand is that when it iterate it will take the first value of (1..10) which is 1 and will pass it to a function like this:
my #array = somefunc(1);
Since that function is not defined, I'll create the logic.
sub somefunc {
my $a = shift;
print $a * $a;
}
which will essentially do this:
1 * 1
and the result is '1'.
To my understanding my #array will look like:
#array = ('1');
And the next line will do:
$AoA[$i] = #array;
I'm assuming that $AoA[$1] is an anonymous array ( he/she didn't declare with 'my', btw) and the #array will be the first element of the this anonymous array which the author said it' WRONG. And the for each loop with iterate to '2'.
somefunc(2);
Which will be '4' and passed to:
$AoA[$i] = #array
What is the point of the author with this code which is wrong. I'm trying to understand what is wrong but more importantly, I'm trying to understand the code. Any help will be appreciated.
UPDATE
I think I understand why this is a common mistake because when I use print and Dumper, I can visually see what the author is trying to convey, here is the revised code.
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
for my $i (1..10) {
my #AoA;
my #array = somefunc($i);
print "The array is Dumper(#array)\n";
$AoA[$i] = #array; # WRONG!
print Dumper($AoA[$i]);
}
sub somefunc {
my $a = shift;
return $a * $a;
}
In the Common Mistakes paragraph of perldoc perldsc, he/she states
Here's the case where you just get the count instead of a nested array:
Below is the output of the Dumper.
The array is Dumper(1)
$VAR1 = 1;
The array is Dumper(4)
$VAR1 = 1;
The array is Dumper(9)
$VAR1 = 1;
The array is Dumper(16)
$VAR1 = 1;
The array is Dumper(25)
$VAR1 = 1;
The array is Dumper(36)
$VAR1 = 1;
The array is Dumper(49)
$VAR1 = 1;
The array is Dumper(64)
$VAR1 = 1;
The array is Dumper(81)
$VAR1 = 1;
The array is Dumper(100)
$VAR1 = 1;
So I'm assuming that the repeated
$VAR1 = 1;
is the count and not the nested array.
The author did indicate that if the count is what I truly want then to rewrite the code like this:
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
for my $i (1..10) {
my #count;
my #array = somefunc($i);
print "The array is Dumper(#array)\n";
$count[$i] = scalar #array;
print Dumper($count[$i]);
}
sub somefunc {
my $a = shift;
return $a * $a;
}
But the documentation didn't tell me how to get the nested array?
UPDATE
Correct me if I am wrong but I rewrote the code to get the nested array:
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my #count;
my #new_array;
for my $i (1..10) {
#my #count;
my #array = somefunc($i);
push #new_array, [#array];
}
sub somefunc {
my $a = shift;
return $a * $a;
}
print Dumper(\#new_array);
Which printed
$VAR1 = [
[
1
],
[
4
],
[
9
],
[
16
],
[
25
],
[
36
],
[
49
],
[
64
],
[
81
],
[
100
]
];
In the following statement:
$AoA[$i] = #array;
the #array is referenced in a scalar context, yielding a number of its elements. The context is imposed by LHS, that is $AoA[$i] which is a single element of the #AoA array.
In Perl, there are no array of arrays in a strict sense. These are emulated essentially by either "flatten" arrays or array with references. For the latter, you would need to use take reference operator as in:
$AoA[$i] = \#array;
For the starter, you may find, that Data::Dumper is very handy in examining complex data stuctures such as arrayrefs and hashrefs.
Perl is polymorphic, which means that it deals with different data types transparently, and makes what is usually a pretty good guess on how to deal with something. This makes the programmer's work much easier because it is not strongly typed like other languages.
So for example if $i is the number 4, you can do this:
print $i + 1;
and you will see a 5 - pretty logical, right?
and if you do this:
print "I am " , $i , " years old";
You will see "I am 4 years old" - in this case perl says "you are operating in list context, so I will treat $i as a string. No need to convert the number into a string as many other languages insist.
So when you assign
$AoA[$i] = #array;
The way it treats this depends on the context. In scalar context, it will set $AoA[$i] to be the length of the array.
For more information about scalar vs list context, read this answer:
http://perl.plover.com/context.html
Your example isn't very useful in understanding what is going on here as your subroutine always returns "1" - the result of calling print(). If you replace the print() with return() then you will at get different values (1, 4, 9, etc).
But the next line of code:
$AoA[$i] = #array;
Will always assign 1 to the element of #Aoa. That's because You are assigning an array (#array) to a scalar variable ($AoA[$i]) and when you evaluate an array in a scalar context, you get the number of elements in the array.
Now, as your #array only ever has a single element, you could do this:
$AoA[$i] = $array[0];
But that's not really building an array of arrays. What you really want to do is to get a reference to an array.
$AoA[$i] = \#array;
This would be more useful if your subroutine returned more than one value.
sub somefunc {
# Used $x instead of $a as $a has a special meaning in Perl
my $x = shift;
return ($x * $x, $x * $x * $x);
}
for my $i (1..10) {
my #array = somefunc($i);
$AoA[$i] = \#array;
}
As useful tool for exploring this is Data::Dumper. Try adding:
use Data::Dumper;
To the top of your code and:
print Dumper #AoA;
After the foreach loop to see the different data structures that you get back.

How can I get this basic Perl sub program that sorts to work properly?

I am brand new to Perl. Can someone help me out and give me a tip or a solution on how to get this sorting sub program to work. I know it has something to do with how arrays are passed to sub programs. I searched online and did not find an answer that I was satisfied with... I also like the suggestions the helpful S.O. users give me too. I would like to have the program print the sorted array in the main sub program. Currently, it is printing the elements of the array #a in original order. I want the sub program to modify the array so when I print the array it is in sorted order. Any suggestions are appreciated. Of course, I want to see the simplest way to fix this.
sub sort {
my #array = #_;
my $i;
my $j;
my $iMin;
for ( $i = 0; $i < #_ - 1; $i++ ) {
$iMin = $i;
for ( $j = $i + 1; $j < #_; $j++ ) {
if ( $array[$j] < $array[$iMin] ) {
$iMin = $j;
}
}
if ( $iMin != $i ) {
my $temp = $array[$i];
$array[$i] = $array[$iMin];
$array[$iMin] = $temp;
}
}
}
Then call from a main sub program:
sub main {
my #a = (-23,3,234,-45,0,32,12,54,-10000,1);
&sort(#a);
my $i;
for ( $i = 0; $i < #a; $i++ ) {
print "$a[$i]\n";
}
}
main;
When your sub does the following assignment my #array = #_, it is creating a copy of the passed contents. Therefore any modifications to the values of #array will not effect #a outside your subroutine.
Following the clarification that this is just a personal learning exercise, there are two solutions.
1) You can return the sorted array and assign it to your original variable
sub mysort {
my #array = #_;
...
return #array;
}
#a = mysort(#a)
2) Or you can pass a reference to the array, and work on the reference:
sub mysort {
my $arrayref = shift;
...
}
mysort(\#a)
Also, it's probably a good idea to not use a sub named sort since that's that's a builtin function. Duplicating your code using perl's sort:
#a = sort {$a <=> $b} #a;
Also, the for loops inside your sub should be rewritten to utilize the last index of an #array, which is written as $#array, and the range operator .. which is useful for incrementors :
for ( my $j = $i + 1; $j <= $#array; $j++ ) {
# Or simpler:
for my $j ($i+1 .. $#array) {
And finally, because you're new, I should pass on that all your scripts should start with use strict; and use warnings;. For reasons why: Why use strict and warnings?
With very few, rare exceptions the simplest (and easiest) way to sort stuff in perl is simply to use the sort builtin.
sort takes an optional argument, either a block or a subname, which can be used to control how sort evaluates which of the two elements it is comparing at any given moment is greater.
See sort on perldoc for further information.
If you require a "natural" sort function, where you get the sequence 0, 1, 2, 3, ... instead of 0, 1, 10, 11, 12, 2, 21, 22, 3, ..., then use the perl module Sort::Naturally which is available on CPAN (and commonly available as a package on most distros).
In your case, if you need a pure numeric sort, the following will be quite sufficient:
use Sort::Naturally; #Assuming Sort::Naturally is installed
sub main {
my #a = (-23,3,234,-45,0,32,12,54,-10000,1);
#Choose one of the following
#a = sort #a; #Sort in "ASCII" ascending order
#a = sort { $b cmp $a } #a; #Sort in reverse of the above
#a = nsort #a; #Sort in "natural" order
#a = sort { ncmp($b, $a) } #a; #Reverse of the above
print "$_\n" foreach #a; #To see what you actually got
}
It is also worth mentioning the use sort 'stable'; pragma which can be used to ensure that sorting occurs using a stable algorithm, meaning that elements which are equal will not be rearranged relative to one another.
As a bonus, you should be aware that sort can be used to sort data structures as well as simple scalars:
#Assume #a is an array of hashes
#a = sort { $a->{name} cmp $b->{name} } #; #Sort #a by name key
#Sort #a by name in ascending order and date in descending order
#a = sort { $a->{name} cmp $b->{name} || $b->{date} cmp $a->{date} } #a;
#Assume #a is an array of arrays
#Sort #a by the 2nd element of the arrays it contains
#a = sort { $a->[1] cmp $b->[1] } #a;
#Assume #a is an array of VERY LONG strings
#Sort #a alphanumerically, but only care about
#the first 1,000 characters of each string
#a = sort { substr($a, 0, 1000) cmp substr($b, 0, 1000) } #a;
#Assume we want to "sort" an array without modifying it:
#Yes, the names here are confusing. See below.
my #idxs = sort { $a[$a] cmp $a[$b] } (0..$#a);
print "$a[$_]\n" foreach #idxs;
##idxs contains the indexes to #a, in the order they would have
#to be read from #a in order to get a sorted version of #a
As a final note, please remember that $a and $b are special variables in perl, which are pre-populated in the context of a sorting sub or sort block; the upshot is that if you're working with sort you can always expect $a and $b to contain the next two elements being compared, and should use them accordingly, but do NOT do my $a;, e.g., or use variables with either name in non-sort-related stuff. This also means that naming things %a or #a, or %b or #b, can be confusing -- see the final section of my example above.

sorting an array on the first number found in each element

I'm looking for help sorting an array where each element is made up of "a number, then a string, then a number". I would like to sort on the first number part of the array elements, descending (so that I list the higher numbers first), while also listing the text etc.
am still a beginner so alternatives to the below are also welcome
use strict;
use warnings;
my #arr = map {int( rand(49) + 1) } ( 1..100 ); # build an array of 100 random numbers between 1 and 49
my #count2;
foreach my $i (1..49) {
my #count = join(',', #arr) =~ m/$i,/g; # maybe try to make a string only once then search trough it... ???
my $count1 = scalar(#count); # I want this $count1 to be the number of times each of the numbers($i) was found within the string/array.
push(#count2, $count1 ." times for ". $i); # pushing a "number then text and a number / scalar, string, scalar" to an array.
}
#for (#count2) {print "$_\n";}
# try to add up all numbers in the first coloum to make sure they == 100
#sort #count2 and print the top 7
#count2 = sort {$b <=> $a} #count2; # try to stop printout of this, or sort on =~ m/^anumber/ ??? or just on the first one or two \d
foreach my $i (0..6) {
print $count2[$i] ."\n"; # seems to be sorted right anyway
}
First, store your data in an array, not in a string:
# inside the first loop, replace your line with the push() with this one:
push(#count2, [$count1, $i];
Then you can easily sort by the first element of each subarray:
my #sorted = sort { $b->[0] <=> $a->[0] } #count2;
And when you print it, construct the string:
printf "%d times for %d\n", $sorted[$i][0], $sorted[$i][1];
See also: http://perldoc.perl.org/perlreftut.html, perlfaq4
Taking your requirements as is. You're probably better off not embedding count information in a string. However, I'll take it as a learning exercise.
Note, I am trading memory for brevity and likely speed by using a hash to do the counting.
However, the sort could be optimized by using a Schwartzian Transform.
EDIT: Create results array using only numbers that were drawn
#!/usr/bin/perl
use strict; use warnings;
my #arr = map {int( rand(49) + 1) } ( 1..100 );
my %counts;
++$counts{$_} for #arr;
my #result = map sprintf('%d times for %d', $counts{$_}, $_),
sort {$counts{$a} <=> $counts{$b}} keys %counts;
print "$_\n" for #result;
However, I'd probably have done something like this:
#!/usr/bin/perl
use strict; use warnings;
use YAML;
my #arr;
$#arr = 99; #initialize #arr capacity to 100 elements
my %counts;
for my $i (0 .. 99) {
my $n = int(rand(49) + 1); # pick a number
$arr[ $i ] = $n; # store it
++$counts{ $n }; # update count
}
# sort keys according to counts, keys of %counts has only the numbers drawn
# for each number drawn, create an anonymous array ref where the first element
# is the number drawn, and the second element is the number of times it was drawn
# and put it in the #result array
my #result = map [$_, $counts{$_}],
sort {$counts{$a} <=> $counts{$b} }
keys %counts;
print Dump \#result;